結城浩のはてなブログ

ふと思いついたことをパタパタと書いてます。

undefについて

−−−2011-07-14,07-17 注意書き始まり−−−
以下の記事は混乱と誤りを含んでいますので、参考にしないでください。
タイトルも誤解を生むというご指摘をいただき変更しました。
以前のタイトルは「Perlでundefが真になる場合」および「Perlでは(undef)は真」でした。
これについて、@akajiroさんからPerlではundefは常に偽と判定される。ただ、条件にリスト代入(これはスカラーコンテキストでは右辺で生成された要素の数を返す)なんかを用いるような場合に注意が必要なことを指して、いつも偽とは限らない、みたいな誇張表現をする人はいる。というコメントをいただいています。(さらに続きのコメント)
−−−2011-07-14,07-17 注意書き終わり−−−
(注意:以下の記述では結城がかなり混乱していますので参考にしないでください。要するに結城が「return undefするな、return ()せよ」というTipsの良い例を作れなかったのが混乱に拍車をかけています。すみませんでした。良い例はperl - (undef) is trueでDanさんが作ってくださいましたので、そちらをご覧ください)

# (1)
while (undef) {
    print "while\n";
    last;
}
for (undef) {
    print "for\n";
    last;
}

上のPerlプログラム(1)を実行すると、以下のように表示されます。

for

理由は、undefはリストコンテキストで真として評価されるから。
ここから「return undefするな、return ()せよ」というTipsが……っていうのは最近どこかのページで読んだ話題なんだけれど、思い出せない。YAPCのどこかだっただろうか。
追記と疑問:id:hirataraさんからご指摘を受けて、別の例(2)を作成…と思ったのですが、これもまた「undefをリストコンテキストで評価」というよりも「(undef)をリストコンテキストで評価」しているような気がしてきました。偽の意味でundefをreturnしない、というTipsはよいのですが、うまい例が作れない……。

# (2)
sub foo {
    return undef;
}

if (@a = &foo) {
    print "1: true";
} else {
    print "2: false";
}

print "\n";

if ($a = &foo) {
    print "3: true";
} else {
    print "4: false";
}

実行結果です。

1: true
4: false

さらに追記:undefとリストコンテキストで「あるリストがスカラーコンテキストで真または偽と評価される、ということはあると思いますが、リストコンテキストで真/偽、という評価のされ方はあるのでしょうか」と言及。確かに。真/偽というからにはブーリアンコンテキストか。どうも私の頭は眠っているようですね。ちょっと頭を冷やすと、こうかな(怪しかったら教えてください)。

  • (a) 関数で偽のつもりでundefを返すのは危険なことがあるよ。返すなら () がよいよ。
  • (b) たとえば、関数fooがundefを返したとする。もしもif (@a = foo)などとしていたら、この条件は真になっちゃうよ。
  • (c) なぜかというと、@a = undefを行うと、@a = (undef)の意味になり、(undef)は真だからね。

…と書いていて思ったのですが、if (@a = foo)なんてやりますかね? Damianさんのプレゼン資料が見つけられない…適切な例が出ていたっけ?
もっと追記:id:tociyukiさんからトラックバックいただきましたが、最初例(1)でやりたかったのは、whileとforで、whileではスカラーコンテキスト、forではリストコンテキストで評価されることを使い、undefをそれぞれに評価させたかったのです……。まあ、例がまずかったのはその通りで、わやなことになっているわけですが。
ところで、tociyukiさんが「はまった」と書いていらしたPerlのリスト構造の話はおもしろいですね…。私は解説を読むまでわかりませんでした(T_T)。
も一つ追記:perl - (undef) is trueでDanさんが良い例を作ってくださいました。ついうっかりundefを返してしまいそうになるが、それをしてはまずいことになってしまう自然な例。ありがとうございます。なるほど。