ぺんちゃん日記

食と歴史と IT と。 Web の旅人ぺんじろうが好奇心赴くままに彷徨います 。

空文字列と if 文の書き方を一度調べてみる【AutoHotkey】。

f:id:yasushiito:20190403184928p:plain

全然わかってない。

AutoHotkeyを使い始めて半年。
未だに その動作に悩まされてはまることがあります。
特に if 文と文字列の扱いについては、なんとなく雰囲気で書いている感があって動いていないコードを知らず知らずのうちに書き散らかしていることもあります。
これではいかんと思い直したので実際に動く行動書きながら再学習したいと思います。
テストに使ったコードは記事の一番後ろに掲載することにします。
途中で登場する断片的なコードは抜粋したもので、そこだけをコピペしても動きません。
ご了承ください。
それでは参りたいと思います。

空の文字列を変数に代入したい。

まず分からないのが 変数に空文字列が入っていたりいなかったりする場合の if 文の動作です。
そこで 未定義の変数を条件に与えたらどうなるか試してみます。
念のために初期化した変数も条件に与えてみましょう。

def := True
if def
    MsgBox, , 変数真偽値でif。, 変数が初期化されている。 ,5
if !undef
    MsgBox, , 変数真偽値でif。, 初期化されていない変数をnotで判定できるか。 ,5
if !""
    MsgBox, , 真偽値でif。, 空文字列は真か偽か。 ,5

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

問題なく動きます。
メッセージボックスは3つとも表示されました。
一度も宣言されていない変数名を与えても文法エラーとはならずに偽の値として判断されるようです。
なおビックリマークは真偽を反転させる not の意味です。
空の文字列は偽とされることがわかります。

空の文字列を変数に代入したい。

次に空文字列の代入です。
代入の時に引用符がいるのかいらないのか、いまだにわかっていません。
代入した結果をメッセージボックスを表示して確認してみます。
空文字列だけでなく、長さのある文字列を代入した場合にも引用符の必要性の有無を確認してみます。

initstr :=
abc := "abcdefg"
c := "c"
emptystr := ""
MsgBox , , 代入 右側に何もない時 , % initstr ,5
;次の行の 影響はない。

MsgBox, , 代入 “”で囲んで代入した場合。, % abc ,5
;代入された文字列には引用符が含まれていないことを確認。

MsgBox, , 代入 “”で代入した場合。, % emptystr ,5
;代入された文字列には引用符が含まれていないことを確認。

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

変数 abc だけ文字が表示されました。
代入される値は引用符を付けても付けなくても空文字列になるようです。

文字列のサイズを調べる。

ところで私が確認している変数の内容は本当に空文字列でしょうか?
空白が入っているのに視覚的に認知できていないだけではないでしょうか。
文字列の長さを確認することで、 本当に空文字列と言うことを確認します。
実は文字列長の測定も自信がありません。
空文字列の文字の長さが0であることを確認したいです。
文字列のサイズの測り方は複数あるので挙動の違いがないか確認してみます。

MsgBox, , 文字列の長さ確認。(StrLen)。, % StrLen(abc),5
;文字列の長さを測る関数は正しく動いている。

StringLen, ln, c
MsgBox, , 文字列の長さ確認。(StringLen), % ln ,5
;関数ではなくコマンドで書いても結果は同じ。

MsgBox, , 文字列の長さでinistrの確認。, % StrLen(initstr) ,5
MsgBox, , 文字列の長さでemptystrの確認。, % StrLen(emptystr) ,5
MsgBox, , 文字列の長さでabcの確認。, % StrLen(abc) ,5
MsgBox, , 未定義の変数を文字列の長さで計った時。, % StrLen(undef) ,5
;空文字列またはnull? 的な場合は長さ0として扱われることを確認。

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

上記のコードで確認していたところ、 期待通り長さ0を返してくれました。
代入される値が 引用符ありなしどちらも0です。
未定義の変数の長さもゼロです。
もちろん abc は7です。

長さの測り方は strlen と stringlen があります。
前者は関数で後者はコマンドです。
関数は戻り値があるので結果を即座に使えるのでメッセージボックスに表示するにしても一行で書けます。
コマンドでは受け取る変数を指定して 結果を受け取るので1行では書けません。
documentによると結果はどちらも同じのようなので、関数で呼び出した方がスマートにかけますね。
これからは文字列の長さは strlen で行うことにします。

ahkwiki.net

ahkwiki.net

If 文で空の文字列を条件分けする。

いよいよ本丸の空文字列が代入された変数の条件判定です。

これまでの経験で、かっこがあったりなかったり、引用符があったりなかったりで結果が変わってくることが分かっています。
何が正解か分からなくなって不信感しかありません。
まずはかっこありなしと引用符ありなしのパターンを確認してみます。

;If 文で空文字列を判定する時の書き方。
if initstr =
    MsgBox, , 空文字列を判定, 右側に何も書かない。,5
if initstr = ""
    MsgBox, , 空文字列を判定,  引用符で囲むと不成立。,5
;括弧で囲まない場合=の右側はどんなときも文字列として扱われる。
;引用符は書く必要がないというか書いてはいけない。

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

かっこがない場合は引用符を書いてはいけません。

if (initstr =)
    MsgBox, , 括弧で囲んで空文字列を判定, 右側に何も書かない不成立。,5
if (initstr = "")
    MsgBox, , 括弧で囲んで空文字列を判定,  引用符で囲むと成立。,5
;括弧で囲んだ場合、 右辺が文字列とは限らないので引用符が必要。

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

かっこがある場合は引用符を書かなければいけません。

これテストに出ます。

If 文で真偽条件で分岐する。

次は if 文を真偽値で判定した時の格好の有り無しについてです。

if !initstr
    MsgBox, , 空文字列を判定,notでも条件は成立。,5
if (!initstr)
    MsgBox, , 括弧で囲んで空文字列を判定, notでも条件は成立。,5
;真偽だけで=がない場合はかっこがあってもなくても問題ない。

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

こちらはかっこがあってもなくても良いです。
十分にシンプルな状態ならIf 文が括弧の有無で動作が変わるということはないということですね。

複雑な IF 文の書き方を調べる。

そして条件が複雑になった場合の確認です。
これも過去の経験でかっこがないと動かなかった記憶があります。
その時は or でつないだ条件でしたが、 and でも同じはずです。
引用符の有無で結果がどのようになるか調べたいと思います。

;条件が複数になるなど if 文の中が複雑になる時の書き方。
if 1 = 1 and initstr =
    MsgBox, , and で繋ぐ複雑な判定。, 右側に何も書かない時不成立。,5
;かっこがついていない時は比較として装備されるので=の右側は文字列扱い。
;つまり次のように判定される。
;1 = "1 and initstr ="
if 1 = 1 and initstr =""
    MsgBox, , and で繋ぐ複雑な判定。, 引用符で囲んでもうまくいかない。,5
;この場合も同様に次のように判定される。
;1 = "1 and initstr ="""

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

かっこがない場合はどちらも成立できませんでした。

if (1 = 1 and initstr= )
    MsgBox, , and で繋ぐ複雑な判定。, かっこを追加する。右側には何も書かない。,5
;括弧で囲まれているのでカッコ内を実行してから、その結果を真偽値として判定する。
;二つの条件を and でつないで判定している。
;=の右側は文字列である保証はないので引用符が必要なのはこれまでのテストで確認済み。

if (1 = 1 and initstr= "")
    MsgBox, , and で繋ぐ複雑な判定。, 括弧で囲んで引用句を使うと OK。,5
;括弧で囲まれているので instr の右側に引用符を入れることで空文字列と比較できる。

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

かっこがある時は引用符を付けた場合だけ成立しました。
なんでこれだけ動くのだろう?

憮然としたとはまさにこのことで、納得いきませんけど複雑な条件を与える場合は括弧で囲んで引用符を記述することで期待通りに条件分岐できます。
これは確実にテストに出るでしょう。

念のため変数の内容が空の文字列ではなく、内容が詰まっている場合も確認してみます。

;念のため空文字列ではない時も試してみる。
if 1 = 1 and abc = abcdefg
    MsgBox, , and で繋ぐabc判定。, 右側の文字列を引用符で囲まない時不成立。,5

if 1 = 1 and abc = "abcdefg"
    MsgBox, , and で繋ぐabc判定。, 右側を引用符で囲むとき不成立。,5

if (1 = 1 and abc = abcdefg)
    MsgBox, , and で繋ぐabc判定。, 括弧で囲んで引用符では囲まない時不成立。,5

if (1 = 1 and abc = "abcdefg")
    MsgBox, , and で繋ぐabc判定。, 括弧で囲んで引用符でも囲むときok,5

myahk/teststr.ahk at master · yasushiito/myahk · GitHub

こちらの場合も、括弧で囲んで引用符を明記した場合のみ条件が成立します。

考察してみる。

頭悪いのでdocumentを読んでもわかんないんですよね。
何度読んでもわからない。
ソースコードを追いかければ分かるのかもしれませんけど、そこまでの情熱をありません。
そこで自分なりに考えます。

まずAutoHotkeyには文字列型とか数値型の違いはありません。
全ては文字列として扱われます。
変数に数値を代入して比較するコードがあるとします。

if age > 19

人間からは、 変数の中の数値を参照して比較しているように見えますが、変数の中身は文字列"19"と比較されています。
AutoHotkeyは内部的には、変数に保存されている文字列の内容を解釈して、なんとなく数字のようなら文字列を数値化して比較するのでしょう。
だから空文字列または未定義の変数を真偽値として条件分けする場合でも 、一度内容を解析するので両者ともに同じ扱いになるということでしょうか。
なおAutoHotkeyでは、真の値は TRUE だけではなく、数値の0も含まれます。

If 文の複雑な条件の謎の動きですが、少しわかってきました。
if 文は二つの値を比較することしかできないということです。

if left = right

最初に出現したイコールまたは不等号の右と左を分けて、両者を比較するだけというシンプルな構造。
しかもこの時に右側は文字列であるというお約束付き。
内部的にはその文字列を一度解釈して、数値ぽければ数値として比較して、文字列のようなら文字列として比較するという動作をしているようです(もちろん私の想像ですよ?
式の右側は必ず文字列であると決まっているので、引用符は必要ありません。
ここで引用符を書いてしまうと、それも文字列の一部だと認識されます。

つまり、次のような条件文は人間から読むと二つの条件が組み合わさっていますが、

if 1 = 1 and initstr =

AutoHotkeyにとっては文字列比較の条件ひとつだけに見えます。

if 1 = "1 and initstr ="

コメントにもありますが、そんな条件式成立するわけないわ。


ただし if 文の中を括弧で囲むと話は変わってきます。
かっこで囲まれている時は、1度括弧の中身を実行してから、その結果を真偽値で判定します。
イメージとしてはこんな感じです。

a := 1 = 1
b := initstr =""
tmp := a and b
if tmp

一度実行されるということは、 必ず文字列であるというお約束は適用されないことを意味します。
=の右側は変数かもしれないし文字列かもしれない。
この場合は、文字列であることを明示するために引用符が必要です。
シンプルな if 文でも、かっこがあるときは引用符が必要だったのと同じです。

なんかやっとわかってきたぞ。

まとめ。

  • 文字列の代入は:=を使って引用符で囲む。
  • 文字列の長さを調べるのは strlen 関数。
  • if 文は文字列の比較。
  • 式の右辺は文字列として処理される。
  • if 文の中の式が複雑になった時はかっこで囲んで式の結果を判定させる。

それではテストに使ったソースコードです。


この記事に登場するAutohotkey スクリプトについて

この記事の中で私が作成したプログラムは、全て自由に使うことができます。
詳しくはこちら