結城浩のはてな日記 このページをアンテナに追加 RSSフィード

2006年4月6日(Thu)

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を返してしまいそうになるが、それをしてはまずいことになってしまう自然な例。ありがとうございます。なるほど。

hiratarahiratara 2006/04/06 09:55 こんにちわ。

perlでforはforeachの略なので、$_ = undefでループするということではないでしょうか。for(undef,undef)だと二回プリントされます。

return ()は、YAPCのDamianさんのセッションでした。

hyukihyuki 2006/04/06 12:59 hirataraさん、こんにちは。ええと、私の理解では、(1) for (XXXX) のときには、XXXXはリストコンテキストで評価される。(2)リストコンテキストでundefを評価したときそれは真値として扱われる。…それはhirataraさんがお書きになっている内容と異なるでしょうか?  …あ、for (undef) の undefはリスト中の一個の要素、という意味ですかね。そかそか。

hiratarahiratara 2006/04/06 16:42 私の疑問は、「while()の中身は真偽値として評価されるが、for()の中身は真偽値としては評価されないのでは?」ということでした。(undef)がTRUEだからループが走るのではなく、リストが与えられたから走っているのではないかということです。

hyukihyuki 2006/04/06 17:03 はい、そうだと思います。for (undef) の場合、リストが与えられて走っています。

BLUEPIXYBLUEPIXY 2006/07/02 14:04 こんにちは。
>if (@a = foo)なんてやりますかね?
(foo がスカラーを返す関数の時におかしいということなんでしょうけど)
この場合、関数が空リストを返す時、偽という意味でやるんじゃないでしょうか?
return wantarray ? : () : undef;
とすれば、はっきりすると思います・・