nDiki : 2011年05月25日

2011年5月25日 (水)

なぜ Perl で配列に対して defined を使ってはいけないのか?

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 を使わないでねということで。

*1以下 Perl 5.14.0 で確認。

スポンサード リンク

今日のさえずり: 気がついたら後頭部ががっつり禿げて、剃るかバーコードかって選択肢

2011年05月25日

  • 08:57 さて、運転再開してるかな?
  • 09:06 京浜東北線走ってた。混雑度は並より少なめなぐらい。
  • 09:13 気がついたら後頭部ががっつり禿げて、剃るかバーコードかって選択肢。
  • 09:16 という夢を今朝みた。禿げの恐怖ってすごい。
  • 09:45 RT @boku: 数ヶ月前から予告していました新会社、株式会社バスキュール号が本日スタートしました。ニュースでもとりあげていただいているようにmixiさんとの共同設立です。http://s.nikkei.com/lVn1Bk 詳細はまたのちほど。
  • 13:00 RT @mixi_PR: ソーシャルグラフを活用した新的なマーケティングを展開してまいります! |バスキュールとミクシィの合弁会社 「株式会社バスキュール号」設立  http://mixi.co.jp/press/2011/0525/8348/
  • 13:48 defined @a は 'defined(@array) is deprecated' って出るけど、defined @$a は出ない。 #Perl
  • 14:04 塩牛丼セット 520円。 (@ 神戸 らんぷ亭 渋谷並木橋店) http://4sq.com/ioBjBa
  • 14:33 托鉢にひさびさ遭遇。
  • 14:44 インクタンク購入。PGBK がたまってるので今日はバラで。 (@ ビックカメラ 渋谷東口店) http://4sq.com/kR74Yq
  • 15:11 'defined(@array) is deprecated' が入ったのは Perl 5.6.0。 #Perl
  • 15:30 perl -e '@a = (); print defined @a ? 1 : 0; push @a, 1; print defined @a ? 1 : 0; shift @a; print defined @a ? 1 : 0'
  • 15:31 たしかにこれは直感的じゃないね。 #Perl
  • 19:40 退勤。
  • 19:50 Twitter、reply 通知メール送られるようになったんだ。
  • 20:06 無くなってる。 (@ 丸善 エキュート品川店) http://4sq.com/kZ0b6W
  • 20:09 これになってた。 (@ Smith エキュート品川) http://4sq.com/mHMgxb
  • 23:39 あ、そんなオプションがあるんですね。 RT @__gfx__: use warnings FATAL => 'all'; してるとFATALになるので問題はないんだけども。
  • 23:58 イベント用に ID ケース買ったよ。 http://flic.kr/p/9LQm8K
  • 24:09 名札をぶらさげようっていう @smokemonkey のアイデア http://bit.ly/jZ5mrR がいいなと思って。結局同じものになっちゃったけど(スリップオン好き・オレンジ好き・お店にあったなかでこれが一番手頃で好みだったで)。
[ 5月25日全て ]

About Me

Naney Naney (なにい)です。株式会社ミクシィでマネージャー・プロダクトオーナーをしています。

nDiki1999年1月に始めたコンピュータ日誌を前身とする NaneyWeb 日記(兼パーソナルナレッジベース)です。ちょっとしたノートは nNote にあります。

follow us in feedly

※内容は個人的見解であり所属組織とは関係ありません。

月別インデックス
Process Time: 0.071158s / load averages: 0.57, 0.47, 0.48
nDiki by WATANABE Yoshimasa (Naney, Google profile)
Powered by DiKicker