defined の挙動で相談されたのでソースコードを見てみたら、配列に対して defined を呼んでいた。 最近の Perl では配列に対して defined を使うのは非推奨である(perldata や perlfunc 参照)。 ほとんどの人が望むような判定結果は返ってこない*1。
perl -e '@a = (); print defined @a ? 1 : 0; push @a, 1; print defined @a ? 1 : 0; shift @a; print defined @a ? 1 : 0'
配列が空かどうかならスカラーコンテキストで評価するだけで OK なのだが、Perl プログラミング経験上、1度は defined を使用してしまうだろう。 ただ通常は警告が出るのですぐ気がつく。 この警告は Perl 5.6.0 以降で出る。
#!/usr/bin/perl use warnings; use strict; my @a = (); print defined @a ? "defined\n" : "undefined\n"; push @a, 1; print defined @a ? "defined\n" : "undefined\n"; shift @a; print defined @a ? "defined\n" : "undefined\n"; # defined(@array) is deprecated at test.pl line 7. # (Maybe you should just omit the defined()?) # defined(@array) is deprecated at test.pl line 9. # (Maybe you should just omit the defined()?) # defined(@array) is deprecated at test.pl line 11. # (Maybe you should just omit the defined()?) # undefined # defined # defined
しかしながら、配列への参照をデリファレンスしたものに defined を呼んでも警告を出してくれない。 相談ではまっていたのはこのケースだった。
#!/usr/bin/perl use warnings; use strict; my $a = []; print defined @$a ? "defined\n" : "undefined\n"; push @$a, 1; print defined @$a ? "defined\n" : "undefined\n"; shift @$a; print defined @$a ? "defined\n" : "undefined\n"; # undefined # defined # defined
なお配列(とハッシュ)に対する defined はメモリが割り当てられたかどうかを得るために使われていた。 Devel::Peek::Dump の結果を見てみると次のような感じ。 配列に要素を push した後に pop して空にしても、メモリは割り当てられた状態になるので defined が真を返すようになるのである。
#!/usr/bin/perl use warnings; use strict; use Devel::Peek 'Dump'; my @a = (); Dump(\@a); push @a, 1; Dump(\@a); shift @a; Dump(\@a); # SV = IV(0x9a6d064) at 0x9a6d068 # REFCNT = 1 # FLAGS = (TEMP,ROK) # RV = 0x9a7dcd8 # SV = PVAV(0x9a6e0a8) at 0x9a7dcd8 # REFCNT = 2 # FLAGS = (PADMY) # ARRAY = 0x0 # FILL = -1 # MAX = -1 # ARYLEN = 0x0 # FLAGS = (REAL) # SV = IV(0x9a6d184) at 0x9a6d188 # REFCNT = 1 # FLAGS = (TEMP,ROK) # RV = 0x9a7dcd8 # SV = PVAV(0x9a6e0a8) at 0x9a7dcd8 # REFCNT = 2 # FLAGS = (PADMY) # ARRAY = 0x9a78a20 # FILL = 0 # MAX = 3 # ARYLEN = 0x0 # FLAGS = (REAL) # Elt No. 0 # SV = IV(0x9a6d064) at 0x9a6d068 # REFCNT = 1 # FLAGS = (IOK,pIOK) # IV = 1 # SV = IV(0x9a6d064) at 0x9a6d068 # REFCNT = 1 # FLAGS = (TEMP,ROK) # RV = 0x9a7dcd8 # SV = PVAV(0x9a6e0a8) at 0x9a7dcd8 # REFCNT = 2 # FLAGS = (PADMY) # ARRAY = 0x9a78a24 (offset=1) # ALLOC = 0x9a78a20 # FILL = -1 # MAX = 2 # ARYLEN = 0x0 # FLAGS = (REAL)
ちなみに Perl 5.14.0 の pp_hot.c を見ると以下のようになっている。 配列だと AvMAX が 0 以上になっていれば真になる(十分条件)。 上の例でも pop した後も MAX = 2 となっていることから、defined が真を返しているわけだ。
PP(pp_defined) { dVAR; dSP; register SV* sv; bool defined; const int op_type = PL_op->op_type; const bool is_dor = (op_type == OP_DOR || op_type == OP_DORASSIGN); if (is_dor) { PERL_ASYNC_CHECK(); sv = TOPs; if (!sv || !SvANY(sv)) { if (op_type == OP_DOR) --SP; RETURNOP(cLOGOP->op_other); } } else { /* OP_DEFINED */ sv = POPs; if (!sv || !SvANY(sv)) RETPUSHNO; } defined = FALSE; switch (SvTYPE(sv)) { case SVt_PVAV: if (AvMAX(sv) >= 0 || SvGMAGICAL(sv) || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied))) defined = TRUE; break; case SVt_PVHV: if (HvARRAY(sv) || SvGMAGICAL(sv) || (SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_tied))) defined = TRUE; break; case SVt_PVCV: if (CvROOT(sv) || CvXSUB(sv)) defined = TRUE; break; default: SvGETMAGIC(sv); if (SvOK(sv)) defined = TRUE; break; } if (is_dor) { if(defined) RETURN; if(op_type == OP_DOR) --SP; RETURNOP(cLOGOP->op_other); } /* assuming OP_DEFINED */ if(defined) RETPUSHYES; RETPUSHNO; }
結論としては、良い子のみなさんは配列やハッシュに defined を使わないでねということで。
Naney (なにい)です。株式会社ミクシィで SNS 事業の部長をしています。
※本サイトの内容は個人的見解であり所属組織とは関係ありません。