5.6.1

名前

perltrap - Perlの不注意による罠

説明

The biggest trap of all is forgetting to use warnings or use the -w switch; see perllexwarn and perlrun. The second biggest trap is not making your entire program runnable under use strict. The third biggest trap is not reading the list of changes in this version of Perl; see perldelta.

最も大きな罠とは、use warnings あるいは -w スイッチを 使うのを忘れてしまうということです。 perllexwarnperlrun を参照してください。 二番目に大きな罠とは、あなたのプログラム全体を use strict の元で実行しないということです。 三番目の罠は、Perl のこのバージョンでの変更点を読まないということです。 perldelta を参照してください。

awk の罠

awk に慣れた方は、以下のようなことに特に注意してください:

  • Englishモジュールを

        use English;

    のようにしてロードすれば、awk でそうであったように ($/ のような)特殊変数を($RS のような)名前で参照することができます。 詳しくは perlvar を参照してください。

  • Perlでは、すべての単純文(simple statement)の末尾にセミコロンが必要です (ブロックの最後に置かれたときを除きます)。 改行は文の区切りとはなりません。

  • ifwhile ではカーリーブレースが必要です。

  • Perlでは、変数は“$”"か“@”か“%”で始まります。

  • 配列の添え字は 0 から始まります。substr() や index() での文字列の位置も 同様です。

  • 配列の添え字が数値であるか、文字列であるかを決めなければなりません。

  • ハッシュ(連想配列)の値は、単に参照するだけでは存在することに なりません。

  • 比較を文字列によって行うのか、数値によって行うのかを 決めなければなりません。

  • 入力を読み込むだけでは split は行われません。 配列への split は自分で行います。 また、split() 演算子の引数は awk のものと異なっています。

  • 通常、カレント行は $0 ではなく $_ にあります。 一般的に、改行は取り除かれません ($0 には実行しているプログラムの名前があります)。 perlvar を参照してください。

  • $<digit> はフィールドを参照しません。これは 直前に行ったパターンマッチングの部分文字列を参照します。

  • print() 文は、$,$\ に値を設定しない限りフィールド区切り子や レコード区切り子を付加しません。English モジュールを使っていれば、 $OFS や $ORS に対して設定することでも OK です。

  • ファイルに対して出力する前には、そのファイルをあらかじめオープンして おかなければなりません。

  • 範囲演算子は“..”であって、カンマではありません。カンマ演算子は Cと同じような振る舞いをします。

  • マッチ演算子は“=~”であって、“~”ではありません (“~”はCと同様に、1の補数を取る演算子です)。

  • べき乗の演算子は“**”であって、“^”ではありません。 “^”はCと同様、XOR演算子です (awk が基本的に C と非互換であることにお気付きかもしれませんね)。

  • 連接演算子は“.”であって、空文字列ではありません (空文字列を使ってしまうと /pat/ /pat/ が、その三番目のスラッシュが除算演算子と解釈されてしまうので 正しく解析できなくなります。 Perl の字句解析器は "/", "?", ">" といった演算子に対して 多少文脈依存となっています。実際、"." 自身も数値の始まりとなる 可能性もあります)

  • キーワード next, exit, continue の振る舞いが異なります。

  • 以下の変数の働きが異なります。

          Awk       Perl
          ARGC      scalar @ARGV (compare with $#ARGV)
          ARGV[0]   $0
          FILENAME  $ARGV
          FNR       $. - something
          FS        (whatever you like)
          NF        $#Fld, or some such
          NR        $.
          OFMT      $#
          OFS       $,
          ORS       $\
          RLENGTH   length($&)
          RS        $/
          RSTART    length($`)
          SUBSEP    $;
  • $RS に正規表現をセットすることはできません。できるのは文字列だけです。

  • 妙だと思ったときには awk の構文を a2p に通して、 出力されたものを見てみましょう。

C の罠

Cerebral C programmers should take note of the following:

知的な C プログラマは以下のことに注意すべきです:

  • ifwhile にはカーリーブレースが必要です。

  • else if ではなく、elsif を使わなければなりません。

  • C のbreakcontinue は、Perlではそれぞれ lastnext となります。 C とは異なり、これらは do { } while 構文では 使えません

  • switch 文はありません(が、その場(on the fly)で作り上げることは簡単です)。

  • Perl では、変数は“$”か“@”か“%”で始まります。

  • コメントの始まりは、“#”であり、“/*”ではありません。

  • なにかのアドレスを得ることはできません。Perl には似たような演算子である バックスラッシュがありますが、これは参照を生成します。

  • ARGV は大文字でなければなりません。$ARGV[0] が C での argv[1] に相当し、 argv[0] にあたるものは $0 です。

  • System calls such as link(), unlink(), rename(), etc. return nonzero for success, not 0. (system(), however, returns zero for success.)

    link(), unlink(), rename() などのシステムコールは、成功時に 0 ではなく非 0 の値を返します。(但し、system() は成功時に 0 を返します。)

  • シグナルハンドラは、シグナル番号ではなくシグナル名を扱います。 使用できるシグナル名は、kill -l として確かめてください。

sed の罠

Seasoned sed programmers should take note of the following:

熟練した sed プログラマは以下のことに注意すべきです:

  • 置換における後方参照には、“\”ではなく“$”を使います。

  • "(", ")", "|" といったパターンマッチのメタキャラクタは、その直前に バックスラッシュを置く必要はありません。

  • 範囲演算子は ... であって、カンマではありません。

shell の罠

Sharp shell programmers should take note of the following:

鋭いシェルプログラマは以下のことに注意すべきです:

  • バッククォート演算子は、コマンド内にシングルクォートがあっても 変数の展開を行ないます。

  • バッククォート演算子は csh とは違って、返された値を変換しません。

  • シェル (特に csh) は、コマンドラインごとに何段階もの置換を行ないます。 Perl はダブルクォート、バッククォート、 アングルブラケット、検索パターンといった特定の構造でだけ置換を行ないます。

  • シェルは一度に少しずつ解釈を行ないます。 Perl は実行前にプログラム全体をコンパイルします (コンパイル時に実行される BEGIN ブロックを除く)。

  • 引数は $1, $2 などではなく、@ARGV から得られます。

  • 環境変数は、自動的には独立したスカラー変数として利用できるように なりません。

Perl の罠

Practicing Perl Programmers should take note of the following:

実践的な Perl プログラマは以下のことに注意すべきです:

  • 多くの演算子がリストコンテキストとスカラーコンテキストとで 振る舞いが変わることを忘れないでください。 詳しくは perldata を参照してください。

  • 裸の単語、特に全てが小文字のものはできる限り使わないでください。 見た目だけではその「裸の単語」が関数なのか、 文字列なのかが判断できません。文字列にはクォートを、 関数呼び出しには括弧をつければ、迷うこともないでしょう。

  • You cannot discern from mere inspection which builtins are unary operators (like chop() and chdir()) and which are list operators (like print() and unlink()). (Unless prototyped, user-defined subroutines can only be list operators, never unary ones.) See perlop and perlsub.

    組込み関数のどれが(chop() や chdir())のような単項演算子で、 どれが(print() や unlink())のような リスト演算子であるかは見ただけではわかりません (プロトタイプがなければ、ユーザー定義サブルーチンは リスト演算子として のみ 定義でき、単項演算子にはできません)。 perlopperlsub を参照してください。

  • いくつかの関数が $_ や @ARGV などをデフォルトにしていますが、 同じことを期待する他の関数がデフォルトになっていないことを覚えるのに、 辛いタイピングが必要でしょう。

  • <FH> 構造はファイルハンドルではなく、そのハンドルに対する行読み込みの 操作(readline operation)です。 while ループの条件式の中にこのファイル読み込みだけがあった場合には 読み込まれたデータは $_ に代入されます。

        while (<FH>)      { }
        while (defined($_ = <FH>)) { }..
        <FH>;  # データは捨てられる!
  • =~ が必要なところで c<=> を使わない、ということを忘れないでください。 これら二つの構造はかなり違います。

        $x =  /foo/;
        $x =~ /foo/;
  • do {} 構造は、ループ制御を行えるような本当のループではありません。

  • ローカル変数は、my() で済むところではこれで済ませること (使えない場所については、perlform を参照してください)。 local() を使えばグローバル変数に対するローカルな値を与えますが、 動的スコープの不慮の副作用の可能性は、そのままです。

  • モジュールにある export された変数を局所化すると、その export された 値は変更されません。 ローカル名は新しい値の別名(alias)となりますが、 外部名は元々の値の別名のままです。

Perl4 から Perl5 への罠

Practicing Perl4 Programmers should take note of the following Perl4-to-Perl5 specific traps.

実践的な Perl4 プログラマは 以下に挙げる Perl4 と Perl5 の違いに特有な罠に気をつけた 方が良いでしょう。

以下は順不同のリストです。

Discontinuance, Deprecation, and BugFix traps

修正された perl4 のバグや、なくなった perl4 の仕様、 perl5 で仕様の変わったもの。

構文解析に関する罠

新しい構文解析器によって引き起こされるもの。

数値に関する罠

Traps having to do with numerical or mathematical operators.

数値や算術演算子に関する罠。

General data type traps

perlの標準的なデータ型に内包される罠。

Context Traps - scalar, list contexts

リスト内のコンテキストや、スカラー文/宣言に関する罠。

優先順位の罠

構文解析、評価、コードの実行の優先順に関係した罠。

General Regular Expression Traps using s///, etc.

パターンマッチングの仕様に関する罠。

Subroutine, Signal, Sorting Traps

シグナル及びシグナルハンドラ、一般的なサブルーチン、ソート、 ソートのためのサブルーチンに関連した罠。

OS Traps

OS特有の罠。

DBM Traps

dbmopen() の使用や、dbm の実装に関連した罠。

Unclassified Traps

その他の罠。

If you find an example of a conversion trap that is not listed here, please submit it to <perlbug@perl.org> for inclusion. Also note that at least some of these can be caught with the use warnings pragma or the -w switch.

もしここで挙げたリストにはないような変換の罠の 例を見つけたら、それを <perlbug@perl.org> まで送ってください。 また、少なくともこれらのいくつかは use warnings プラグマか -w スイッチで捕捉できることに注意してください。

Discontinuance, Deprecation, and BugFix traps

perl4 から、なくなったり修正されたことがら。

  • Discontinuance

    “_”で始まるシンボルはもはや main パッケージに強制的に結び付けられることは、 $_ 自身(と @- など)を除いてはなくなりました。

        package test;
        $_legacy = 1;
    
        package main;
        print "\$_legacy is ",$_legacy,"\n";
    
        # perl4 の出力: $_legacy is 1
        # perl5 の出力: $_legacy is
  • Deprecation

    ダブルコロンは、変数名の中でパッケージセパレーターになるようになりました。 このため、以下の例では perl4 と perl5 とでは振る舞いが変わります。 これはパッケージが実在してないためです。

        $a=1;$b=2;$c=3;$var=4;
        print "$a::$b::$c ";
        print "$var::abc::xyz\n";
    
        # perl4 の出力: 1::2::3 4::abc::xyz
        # perl5 出力: 3

    :: は今では、(これがバグとして分類すべきかどうかはともかく) パッケージデリミタとしてみなされるようになっています (ここでは古いパッケージデリミタの ' を使っています)。 $x = 10 ; print "x=${'x}\n" ;

        # perl4 の出力: x=10
        # perl5 の出力: Can't find string terminator "'" anywhere before EOF

    You can avoid this problem, and remain compatible with perl4, if you always explicitly include the package name:

    常にパッケージ名を含めることで、この問題を避けつつ、perl4 との 互換性を維持することができます:

        $x = 10 ;
        print "x=${main'x}\n" ;

    $: の解析については、優先順位の罠 も参照してください。

  • BugFix

    splice() の第 2、第 3 引数は (ラクダ本にある通り)リストコンテキストではなく スカラーコンテキストで評価されるようになりました。

        sub sub1{return(0,2) }          # 要素二つのリストを返す
        sub sub2{ return(1,2,3)}        # 要素三つのリストを返す
        @a1 = ("a","b","c","d","e");
        @a2 = splice(@a1,&sub1,&sub2);
        print join(' ',@a2),"\n";
    
        # perl4 の出力: a b
        # perl5 の出力: c d e
  • Discontinuance

    最適化によってなくなってしまうようなブロックの中に 飛び込むgoto は使えなくなりました。ちぇっ。

        goto marker1;
    
        for(1){
        marker1:
            print "Here I is!\n";
        }
    
        # perl4 prints: Here I is!
        # perl5 errors: Can't "goto" into the middle of a foreach loop
  • Discontinuance

    空白を変数の名前や、クォート構造の区切りに使うことは 構文的に正当なものではなくなりました。ちぇっ、ちぇっ。

        $a = ("foo bar");
        $b = q baz ;
        print "a is $a, b is $b\n";
    
        # perl4 の出力: a is foo bar, b is baz
        # perl5 errors: Bareword found where operator expected
  • Discontinuance

    古い while/if BLOCK BLOCK の構文は、もはやサポートされていません。

        if { 1 } {
            print "True!";
        }
        else {
            print "False!";
        }
    
        # perl4 の出力: True!
        # perl5 errors: syntax error at test.pl line 1, near "if {"
  • BugFix

    ** 演算子の優先順位は、単項のマイナスよりも高くなりました。 これは以前からドキュメントにはそうあったのですが、実際は違っていました。

        print -4**2,"\n";
    
        # perl4 の出力: 16
        # perl5 の出力: -16
  • Discontinuance

    配列ではないリストに対する繰り返しで あるときの foreach{} の意味が変わりました。 以前はそういったリストはテンポラリな配列に代入されていましたが、 現在はそうではありません(効率上の理由です)。 これは、現在では値のコピーに対して繰り返しをするのではなく 実際の値に対して繰り返しをするということです。

        @list = ('ab','abc','bcd','def');
        foreach $var (grep(/ab/,@list)){
            $var = 1;
        }
        print (join(':',@list));
    
        # perl4 の出力: ab:abc:bcd:def
        # perl5 の出力: 1:1:bcd:def

    perl4 と同じようにするには、自分で陽にテンポラリの配列へと コピーしてからその配列に対して繰り返しを行います。例えば、

        foreach $var (grep(/ab/,@list)){

    この部分を以下のように変更します。

        foreach $var (@tmp = grep(/ab/,@list)){

    そうしないと、$var を変更したときに @list の値に影響が出ます (これはループ変数に $_ を使っていて、かつ、 $_ を局所化していないようなサブルーチンを ループの中で呼ぶようなときに良く起こります)。

  • Discontinuance

    引数なしの split の振る舞いは、split /\s+/ ($_ が空白から始まっているときに先頭のフィールドが空になる) のときと同じというものから、split ' ' ($_ が空白から始まっているときに先頭のフィールドが空にならない) のときと同じものになりました。

        $_ = ' hi mom';
        print join(':', split);
    
        # perl4 の出力: :hi:mom
        # perl5 の出力: hi:mom
  • BugFix

    perl4 では、-e スイッチにアタッチされたテキストは無視されて、常に 後に続く引数からプログラムが取り出されていました。さらに、 -e スイッチの後に引数を渡さない場合も受け付けていました。 これらの振る舞いは両方とも修正されました。

        perl -e'print "attached to -e"' 'print "separate arg"'
    
        # perl4 の出力: separate arg
        # perl5 の出力: attached to -e
    
        perl -e
    
        # perl4 prints:
        # perl5 dies: No code specified for -e.
  • Discontinuance

    perl4 では push の戻り値はドキュメントに書かれていませんでしたが、 実際には対象となるリストに最後にpushした値が返されていました。 Perl5 では、push の戻り値はドキュメントに明記され、かつそれは perl4 から変更されました。これは push した後のリストにある要素の数を 返します。

        @x = ('existing');
        print push(@x, 'first new', 'second new');
    
        # perl4 の出力: second new
        # perl5 の出力: 3
  • Deprecation

    一部のエラーメッセージが異なっています。

  • Discontinuance

    In Perl 4, if in list context the delimiters to the first argument of split() were ??, the result would be placed in @_ as well as being returned. Perl 5 has more respect for your subroutine arguments.

    Perl 4 では、リストコンテキストで split() の最初の引数の デリミタが ?? だった場合、返される結果が @_ にも設定されました。 Perl 5 ではサブルーチンの引数により多くの敬意を払います。

  • Discontinuance

    幾つかのバグがうかつにも修正されているかもしれません :-)

パースの罠

Perl4-to-Perl5 traps from having to do with parsing.

パースに関する Perl4 と Perl5 の違いの罠です。

  • Parsing

    . と = の間にあるスペースに注意。

        $string . = "more string";
        print $string;
    
        # perl4 の出力: more string
        # perl5 の出力: syntax error at - line 1, near ". ="
  • Parsing

    perl5 では構文解析が改良されました。

        sub foo {}
        &foo
        print("hello, world\n");
    
        # perl4 の出力: hello, world
        # perl5 の出力: syntax error
  • Parsing

    “それが関数に見えるのなら、それは関数だ”のルール。

      print
        ($foo == 1) ? "is one\n" : "is zero\n";
    
        # perl4 の出力: is zero
        # perl5 の警告: "Useless use of a constant in void context" if using -w
  • Parsing

    $#array 構造の文字列展開で名前の周りにブレースがあるときには 違いがあります。

        @a = (1..3);
        print "${#a}";
    
        # perl4 prints: 2
        # perl5 fails with syntax error
    
        @ = (1..3);
        print "$#{a}";
    
        # perl4 prints: {a}
        # perl5 prints: 2

数値の罠

Perl4-to-Perl5 traps having to do with numerical operators, operands, or output from same.

同じものに対する数値演算子、オペランド、出力に関する Perl4 と Perl5 の違いの罠です。

  • Numerical

    Formatted output and significant digits

    書式指定された出力と、最下位の数字。

        print 7.373504 - 0, "\n";
        printf "%20.18f\n", 7.373504 - 0;
    
        # Perl4 の出力:
        7.375039999999996141
        7.37503999999999614
    
        # Perl5 の出力:
        7.373504
        7.37503999999999614
  • Numerical

    ここに記述されていた項目は削除されました。 以前あったものでは、オートインクリメント演算子が符号付き整数の限界を越えたことを 検知しないということを紹介していました。 これはバージョン 5.003_04 で修正されましたが、大きな整数を取り扱うことに 不安を感じているのであれば

       use Math::BigInt;

    を使うことを考慮してください。

  • Numerical

    数値の等価性の比較の結果を代入しても、perl ではその比較の結果が 偽 (0) であったときにはうまくいきません。 論理比較は、現在 0 ではなく null を返します。

        $p = ($test == 1);
        print $p,"\n";
    
        # perl4 の出力: 0
        # perl5 出力:

    この新しい仕様の別の例は "//, etc."" in "General Regular Expression Traps using s を参照してください。

  • Bitwise string ops

    When bitwise operators which can operate upon either numbers or strings (& | ^ ~) are given only strings as arguments, perl4 would treat the operands as bitstrings so long as the program contained a call to the vec() function. perl5 treats the string operands as bitstrings. (See "Bitwise String Operators" in perlop for more details.)

    数値としても文字列としても操作可能なビット操作演算子 (& | ^ ~) が 文字列のみを引数として与えられた場合、 perl4 は、プログラムが vec() 関数の呼び出しを含んでいればオペランドを ビット文字列として扱います。 perl5 は文字列オペランドをビット文字列として扱います (さらなる詳細については "Bitwise String Operators" in perlop を参照してください)。

        $fred = "10";
        $barney = "12";
        $betty = $fred & $barney;
        print "$betty\n";
        # Uncomment the next line to change perl4's behavior
        # ($dummy) = vec("dummy", 0, 0);
    
        # Perl4 prints:
        8
    
        # Perl5 prints:
        10
    
        # If vec() is used anywhere in the program, both print:
        10

一般的なデータ型の罠

Perl4-to-Perl5 traps involving most data-types, and their usage within certain expressions and/or context.

ほとんどのデータ型と、それらの特定の表現やコンテキストでの 使い方に関する Perl4 と Perl5 の違いの罠です。

  • (配列)

    Negative array subscripts now count from the end of the array.

    配列の添え字が負であったとき、それは配列の終端から数えられるように なりました。

        @a = (1, 2, 3, 4, 5);
        print "The third element of the array is $a[3] also expressed as $a[-2] \n";
    
        # perl4 の出力: The third element of the array is 4 also expressed as
        # perl5 の出力: The third element of the array is 4 also expressed as 4
  • (配列)

    $#array に(それまでよりも)小さな値を設定したときには、余計な 配列要素は捨てられ、さらにそれが元に戻せないようになりました。

        @a = (a,b,c,d,e);
        print "Before: ",join('',@a);
        $#a =1;
        print ", After: ",join('',@a);
        $#a =3;
        print ", Recovered: ",join('',@a),"\n";
    
        # perl4 の出力: Before: abcde, After: ab, Recovered: abcd
        # perl5 の出力: Before: abcde, After: ab, Recovered: ab
  • (ハッシュ)

    ハッシュは使われる前に定義されます。

        local($s,@a,%h);
        die "scalar \$s defined" if defined($s);
        die "array \@a defined" if defined(@a);
        die "hash \%h defined" if defined(%h);
    
        # perl4 の出力:
        # perl5 dies: hash %h defined

    Perl will now generate a warning when it sees defined(@a) and defined(%h).

    Perl は defined(@a) と defined(%h) に警告を出すようになりました。

  • (グロブ)

    ある変数から別の変数へのグロブの代入は、 代入された変数が代入の後で局所化されているときには 失敗します。

        @a = ("This is Perl 4");
        *b = *a;
        local(@a);
        print @b,"\n";
    
        # perl4 の出力: This is Perl 4
        # perl5 の出力:
  • (グロブ)

    Assigning undef to a glob has no effect in Perl 5. In Perl 4 it undefines the associated scalar (but may have other side effects including SEGVs). Perl 5 will also warn if undef is assigned to a typeglob. (Note that assigning undef to a typeglob is different than calling the undef function on a typeglob (undef *foo), which has quite a few effects.

    undef のグロブへの代入は Perl5 では何の影響も及ぼしません。 Perl4 では結び付けられたスカラーを undefine します (しかし SEGV を含め、なんらかの副作用があるかもしれません)。 Perl 5 はまた型グロブに undef を代入すると警告されます。 (型グロブに undef を代入するのは、型グロブに対して undef 関数を呼び出すのとは違います)。いくつかの効果があります。

        $foo = "bar";
        *foo = undef;
        print $foo;
    
        # perl4 prints:
        # perl4 warns: "Use of uninitialized variable" if using -w
        # perl5 prints: bar
        # perl5 warns: "Undefined value assigned to typeglob" if using -w
  • (スカラー文字列)

    (文字列に対する)単項の符号反転の意味が変わりました。 この変更は戻り値と、マジックインクリメントの両方に影響します。

        $x = "aaa";
        print ++$x," : ";
        print -$x," : ";
        print ++$x,"\n";
    
        # perl4 の出力: aab : -0 : 1
        # perl5 の出力: aab : -aab : aac
  • (定数)

    perl4 では定数を変更してしまいます。

        $foo = "x";
        &mod($foo);
        for ($x = 0; $x < 3; $x++) {
            &mod("a");
        }
        sub mod {
            print "before: $_[0]";
            $_[0] = "m";
            print "  after: $_[0]\n";
        }
    
        # perl4:
        # before: x  after: m
        # before: a  after: m
        # before: m  after: m
        # before: m  after: m
    
        # Perl5:
        # before: x  after: m
        # Modification of a read-only value attempted at foo.pl line 12.
        # before: a
  • (スカラー)

    perl4 と perl5 とで全然違う動作:

        print "$x", defined $x
    
        # perl 4: 1
        # perl 5: <no output, $x is not called into existence>
  • (変数の自殺)

    perl5 では、変数の自殺(variable sucide)の振る舞いはより 首尾一貫したものとなりました。 perl4 ではスカラーのみがそうであるような振る舞いを、 perl5 ではハッシュとスカラーで示します。

        $aGlobal{ "aKey" } = "global value";
        print "MAIN:", $aGlobal{"aKey"}, "\n";
        $GlobalLevel = 0;
        &test( *aGlobal );
    
        sub test {
            local( *theArgument ) = @_;
            local( %aNewLocal ); # perl 4 != 5.001l,m
            $aNewLocal{"aKey"} = "this should never appear";
            print "SUB: ", $theArgument{"aKey"}, "\n";
            $aNewLocal{"aKey"} = "level $GlobalLevel";   # 出力すべきもの
            $GlobalLevel++;
            if( $GlobalLevel<4 ) {
                &test( *aNewLocal );
            }
        }
    
        # Perl4:
        # MAIN:global value
        # SUB: global value
        # SUB: level 0
        # SUB: level 1
        # SUB: level 2
    
        # Perl5:
        # MAIN:global value
        # SUB: global value
        # SUB: this should never appear
        # SUB: this should never appear
        # SUB: this should never appear

コンテキストの罠 - スカラーコンテキストとリストコンテキスト

  • (リストコンテキスト)

    The elements of argument lists for formats are now evaluated in list context. This means you can interpolate list values now.

    formatに対する引数リストの要素は、リストコンテキストで 評価されるようになりました。これは、今やリストの値が 展開できるようになったということです。

        @fmt = ("foo","bar","baz");
        format STDOUT=
        @<<<<< @||||| @>>>>>
        @fmt;
        .
        write;
    
        # perl4 errors:  Please use commas to separate fields in file
        # perl5 の出力: foo     bar      baz
  • (スカラーコンテキスト)

    caller() 関数は呼び出し元がなく、スカラーコンテキストで呼ばれた 場合には偽を返すようになりました。これによりライブラリファイルが (自分が)require されたのかを判断することができます。

        caller() ? (print "You rang?\n") : (print "Got a 0\n");
    
        # perl4 errors: There is no caller
        # perl5 の出力: Got a 0
  • (スカラーコンテキスト)

    スカラーコンテキストにあるカンマ演算子は、その引数に対して スカラーコンテキストを与えるようになりました。

        @y= ('a','b','c');
        $x = (1, 2, @y);
        print "x = $x\n";
    
        # Perl4 prints:  x = c   # Thinks list context interpolates list
        # Perl5 prints:  x = 3   # Knows scalar uses length of list
  • (list, builtin)

    sprintf() is prototyped as ($;@), so its first argument is given scalar context. Thus, if passed an array, it will probably not do what you want, unlike Perl 4:

    sprintf() のプロトタイプは ($;@) なので、最初の引数は スカラコンテキストです。従って、配列を渡すと、 Perl 4 とは違っておそらくあなたの望まない結果になるでしょう。

        @z = ('%s%s', 'foo', 'bar');
        $x = sprintf(@z);
        print $x;
    
        # perl4 prints: foobar
        # perl5 prints: 3

    printf() works the same as it did in Perl 4, though:

    しかし、printf() のほうは Perl 4 と同じように動きます:

        @z = ('%s%s', 'foo', 'bar');
        printf STDOUT (@z);
    
        # perl4 の出力: foobar
        # perl5 の出力: foobar

評価順序の罠

Perl4-to-Perl5 traps involving precedence order.

評価順序に関する Perl4 と Perl5 の違いの罠です。

Perl 4 has almost the same precedence rules as Perl 5 for the operators that they both have. Perl 4 however, seems to have had some inconsistencies that made the behavior differ from what was documented.

Perl4 はほとんどの演算子で Perl5 と同じ優先順位を持っています。 しかし Perl4 では、ドキュメントとは少々異なるような 一貫性に欠けるものがあります。

  • Precedence

    LHS vs. RHS when both sides are getting an op.

    左辺と右辺は両方とも演算子を取るようになりました。

        @arr = ( 'left', 'right' );
        $a{shift @arr} = shift @arr;
        print join( ' ', keys %a );
    
        # perl4 の出力: left
        # perl5 の出力: right
  • Precedence

    以下の例は、優先順位の関係で意味エラー(semantic error)となるようになりました。

        @list = (1,2,3,4,5);
        %map = ("a",1,"b",2,"c",3,"d",4);
        $n = shift @list + 2;   # リストの最初の要素に2を加える
        print "n is $n, ";
        $m = keys %map + 2;     # ハッシュにあるアイテムの数+2
        print "m is $m\n";
    
        # perl4 出力: n is 3, m is 6
        # perl5 errors and fails to compile
  • Precedence

    代入演算子の優先順位は、代入と同じとなりました。 perl4 は間違って、この優先順位が関連演算子と同じものに なっています。このため、式の中にあるのと同じように括弧でくくらなければ ならなくなりました。

        /foo/ ? ($a += 2) : ($a -= 2);

    下のようにしてしまうと、

        /foo/ ? $a += 2 : $a -= 2

    これは

        (/foo/ ? $a += 2 : $a) -= 2;

    のように間違って解析されてしまいます。 その一方で、

        $a += /foo/ ? 1 : 2;

    これは C プログラマーが期待するであろう動作になりました。

  • Precedence

        open FOO || die;

    これはもはや正しくなくなりました。ここではファイルハンドルを 括弧で括る必要があります。さもなければ、perl5 はこういった文を そのデフォルトの優先順位のまま放っておきます(以下の例参照)。

        open(FOO || die);
    
        # perl4 opens or dies
        # perl5 opens FOO, dying only if 'FOO' is false, i.e. never
  • Precedence

    perl4 は特殊変数 $: の優先順位に、perl5 が $:: を main パッケージと みなすのと同じ優先順位を与えます。

        $a = "x"; print "$::a";
    
        # perl 4 の出力: -:a
        # perl 5 の出力: x
  • Precedence

    perl には、ファイルテスト演算子と代入演算子を組み合わせたときに 優先順位のバグがありました。したがって、Perl4 の優先順位テーブルでは -e $foo .= "q"((-e $foo) .= "q") と解釈すべきなのに実際には ((-e $foo) .= "q") と解釈していました。 Perl5 ではドキュメントにある通りの優先順位です。

        -e $foo .= "q"
    
        # perl4 の出力: no output
        # perl5 の出力: Can't modify -e in concatenation
  • Precedence

    In perl4, keys(), each() and values() were special high-precedence operators that operated on a single hash, but in perl5, they are regular named unary operators. As documented, named unary operators have lower precedence than the arithmetic and concatenation operators + - ., but the perl4 variants of these operators actually bind tighter than + - .. Thus, for:

    perl4 では、key()、each()、values() はシングルハッシュに対する 特別に高い評価順序を持った演算子でした。 しかし perl5 では、これらの演算子は通常の名前付き単項演算子となりました。 ドキュメントにある通り、名前付き単項演算子は、+ - . のような 数学演算子や連結演算子よりも低い優先順位を持っています。 しかし、perl4 ではこれらの演算子よりも key() などのほうが強く演算対象と 結び付いていたのです。 したがって、以下の例のようになります:

        %foo = 1..10;
        print keys %foo - 1
    
        # perl4 prints: 4
        # perl5 prints: Type of arg 1 to keys must be hash (not subtraction)

    この perl4 の振る舞いは便利であるかもしれませんが、一貫性に欠けます。

s/// などを使ったときの一般的な正規表現の罠

All types of RE traps.

正規表現に関する全てのタイプの罠。

  • 正規表現

    s'$lhs'$rhs' はもはやいずれの辺にあっても展開されなくなりました。 以前は $lhs は展開し、$rhs を展開しませんでした。 (文字列にあるリテラルの '$' にはいまでもマッチしません)。

        $a=1;$b=2;
        $string = '1 2 $a $b';
        $string =~ s'$a'$b';
        print $string,"\n";
    
        # perl4 の出力: $b 2 $a $b
        # perl5 の出力: 1 2 $a $b
  • 正規表現

    m//g は、その状態を正規表現ではなく検索対象の文字列に 結び付けるようになりました (sub に対するブロックのスコープが残っているのであれば、 検索文字列の状態は失われます)。

        $_ = "ababab";
        while(m/ab/g){
            &doit("blah");
        }
        sub doit{local($_) = shift; print "Got $_ "}
    
        # perl4 prints: Got blah Got blah Got blah Got blah
        # perl5 の出力: infinite loop blah...
  • 正規表現

    現在のところ、m//o 量指定子を無名サブルーチンの中にある正規表現で 使った場合、すべての クロージャーはそのような無名サブルーチンから、 そういったクロージャの中で一番最初に使われたものの中で コンパイルされたかのような正規表現を生成します。

        sub build_match {
            my($left,$right) = @_;
            return sub { $_[0] =~ /$left stuff $right/o; };
        }
        $good = build_match('foo','bar');
        $bad = build_match('baz','blarch');
        print $good->('foo stuff bar') ? "ok\n" : "not ok\n";
        print $bad->('baz stuff blarch') ? "ok\n" : "not ok\n";
        print $bad->('foo stuff bar') ? "not ok\n" : "ok\n";

    For most builds of Perl5, this will print: ok not ok not ok

    この例の場合、build_match() は常に最初に build_match() が呼ばれたときの $left と $right の内容にマッチするようなサブルーチンを返します。 呼び出されたその時点での値ではありません。

  • 正規表現

    マッチングの中で括弧が使われなかった場合、perl4 では $+ には $& と同じようにマッチした全体がセットされますが、 Perl5 ではそうではありません。

        "abcdef" =~ /b.*e/;
        print "\$+ = $+\n";
    
        # perl4 の出力: bcde
        # perl5 の出力:
  • 正規表現

    置換は、失敗したときには空文字列を返すようになりました。

        $string = "test";
        $value = ($string =~ s/foo//);
        print $value, "\n";
    
        # perl4 の出力: 0
        # perl5 の出力:

    この新しい仕様に関しては 数値に関する罠 も参照してください。

  • 正規表現

    s`lhs`rhs` (バッククォートの使用)は通常の置換となり、 バッククォートの展開は行われなくなりました。

        $string = "";
        $string =~ s`^`hostname`;
        print $string, "\n";
    
        # perl4 の出力: <the local hostname>
        # perl5 の出力: hostname
  • 正規表現

    正規表現中の変数の使用に関する構文解析がより厳密になりました。

        s/^([^$grpc]*$grpc[$opt$plus$rep]?)//o;
    
        # perl4: compiles w/o error
        # perl5: with Scalar found where operator expected ..., near "$opt$plus"

    an added component of this example, apparently from the same script, is the actual value of the s'd string after the substitution. [$opt] is a character class in perl4 and an array subscript in perl5

    同じスクリプトでこの例に付け加えことは、 置換後の文字列の実際の値です。 [$opt] は perl4 ではキャラクタクラスであり、perl5 では 配列の添え字となります。

        $grpc = 'a';
        $opt  = 'r';
        $_ = 'bar';
        s/^([^$grpc]*$grpc[$opt]?)/foo/;
        print ;
    
        # perl4 の出力: foo
        # perl5 の出力: foobar
  • Regular Expression

    perl5 では、m?x??x? と同様に一回だけマッチします。 perl4 では、/x/m!x! と同じように何度でもマッチします。

        $test = "once";
        sub match { $test =~ m?once?; }
        &match();
        if( &match() ) {
            # m?x? が二回以上マッチ
            print "perl4\n";
        } else {
            # m?x? が一回だけマッチ
            print "perl5\n";
        }
    
        # perl4 の出力: perl4
        # perl5 の出力: perl5

サブルーチン、シグナル、ソートの罠

The general group of Perl4-to-Perl5 traps having to do with Signals, Sorting, and their related subroutines, as well as general subroutine traps. Includes some OS-Specific traps.

Perl4 と Perl5 の違いの罠に分類される一般的なものは シグナル、ソート、そしてそれらに関連する サブルーチンで、いくつかの OS 固有の罠を含めた サブルーチンの罠と同じ様なものです。

  • (Signals)

    文字列のように見える裸の単語は、その名前が使うよりも前に サブルーチンの名前として定義されている場合にはサブルーチンとして みなされます。

        sub SeeYa { warn"Hasta la vista, baby!" }
        $SIG{'TERM'} = SeeYa;
        print "SIGTERM is now $SIG{'TERM'}\n";
    
        # perl4 prints: SIGTERM is now main'SeeYa
        # perl5 prints: SIGTERM is now main::1 (and warns "Hasta la vista, baby!")

    -w を使って、これを発見できます。

  • (Sort Subroutine)

    reverse はもはやソートのサブルーチンの名前としては 使えなくなりました。

        sub reverse{ print "yup "; $a <=> $b } 
        print sort reverse (2,1,3);
    
        # perl4 prints: yup yup 123
        # perl5 prints: 123
        # perl5 warns (if using -w): Ambiguous call resolved as CORE::reverse()
  • warn() won't let you specify a filehandle.

    常に STDERR に出力していたにもかかわらず、perl4 では warn() はファイルハンドルの 指定を必要としていましたが、perl5 では必要なくなりました。

        warn STDERR "Foo!";
    
        # perl4 の出力: Foo!
        # perl5 の出力: String found where operator expected

OS の罠

  • (SysV)

    Under HPUX, and some other SysV OSes, one had to reset any signal handler, within the signal handler function, each time a signal was handled with perl4. With perl5, the reset is now done correctly. Any code relying on the handler _not_ being reset will have to be reworked.

    HPUX 及び一部の SysV OS では、perl4 のときには シグナルが発生する度にそのシグナルハンドラ関数の中で シグナルハンドラを再設定しなければなりませんでした。 perl5 では、この再設定を正しく行うようになりました。 ハンドラを再設定しないということに依存したプログラムは 作業しなおす必要があります。

    5.002 以降の perl では、SysV のときには sigaction() を使います。

        sub gotit {
            print "Got @_... ";
        }
        $SIG{'INT'} = 'gotit';
    
        $| = 1;
        $pid = fork;
        if ($pid) {
            kill('INT', $pid);
            sleep(1);
            kill('INT', $pid);
        } else {
            while (1) {sleep(10);}
        }
    
        # perl4 (HPUX) prints: Got INT...
        # perl5 (HPUX) prints: Got INT... Got INT...
  • (SysV)

    SysV OS では、 アペンドモード(>>)でオープンしたファイルに対する seek() は fopen() マニュアルページにあるように正しく動作するように なりました。例えば、アペンドモードでファイルをオープンした場合には 既にファイルにある情報を上書きすることはできません。

        open(TEST,">>seek.test");
        $start = tell TEST ;
        foreach(1 .. 9){
            print TEST "$_ ";
        }
        $end = tell TEST ;
        seek(TEST,$start,0);
        print TEST "18 characters here";
    
        # perl4 (solaris) seek.test has: 18 characters here
        # perl5 (solaris) seek.test has: 1 2 3 4 5 6 7 8 9 18 characters here

展開の罠

Perl4-to-Perl5 traps having to do with how things get interpolated within certain expressions, statements, contexts, or whatever.

Perl4 と Perl5 の違いの罠には、式や文、コンテキストなどにおける 展開に関するものがあります。

  • Interpolation

    ダブルクォートで囲まれた文字列にある @ は常に配列に展開されます。

        print "To: someone@somewhere.com\n";
    
        # perl < 5.6.1, error : In string, @somewhere now must be written as \@somewhere
        # perl >= 5.6.1, warning : Possible unintended interpolation of @somewhere in string
  • Interpolation

    ダブルクォートで括られた文字列がエスケープされていない $ や @ で 終了することがなくなりました。

        $foo = "foo$";
        $bar = "bar@";
        print "foo is $foo, bar is $bar\n";
    
        # perl4 の出力: foo is foo$, bar is bar@
        # perl5 errors: Final $ should be \$ or $name

    注意:perl5 は、$bar の終端にある @ については“エラーにしません”

  • Interpolation

    Perl はダブルクォート中にあるブレースの内側の任意の式を 評価するようになりました(一般的には、$@ に続いて 開きのブレースがきたときです)。

        @www = "buz";
        $foo = "foo";
        $bar = "bar";
        sub foo { return "bar" };
        print "|@{w.w.w}|${main'foo}|";
    
        # perl4 の出力: |@{w.w.w}|foo|
        # perl5 の出力: |buz|bar|

    use strict; を使って、perl5 におけるこのような罠を避けることが できることに注意してください。

  • Interpolation

    The construct "this is $$x" used to interpolate the pid at that point, but now tries to dereference $x. $$ by itself still works fine, however.

    "this is $$x" はプロセス ID を展開するようになっていましたが、 今は $x の参照外し(dereference)を試みます。 それでも $$ 自身は今でもきちんと動作します。

        $s = "a reference";
        $x = *s;
        print "this is $$x\n";
    
        # perl4 の出力: this is XXXx   (XXX はカレントプロセスID)
        # perl5 prints: this is a reference
  • Interpolation

    eval "EXPR" を使ったその場(on the fly)でのハッシュの生成は ハッシュの名前を指定する $ がプロテクトされているか、もしくは 両方のカーリーブレースがプロテクトされていることを要求します。 両方のカーリーブレースがプロテクトされている場合には、perl4 と perl5 の結果は同じです。

        $hashname = "foobar";
        $key = "baz";
        $value = 1234;
        eval "\$$hashname{'$key'} = q|$value|";
        (defined($foobar{'baz'})) ?  (print "Yup") : (print "Nope");
    
        # perl4 の出力: Yup
        # perl5 の出力: Nope

    以下のものを:

        eval "\$$hashname{'$key'} = q|$value|";

    次のように変更すると:

        eval "\$\$hashname{'$key'} = q|$value|";

    結果はこうなります:

        # perl4 の出力: Nope
        # perl5 の出力: Yup

    以下のように変更した場合には

        eval "\$$hashname\{'$key'\} = q|$value|";

    結果はこうなります。

        # perl4 の出力: Yup
        # perl5 の出力: Yup
        # これは両方のバージョンで同じ結果となります
  • Interpolation

    以前のバージョンにあったバグに依存しているような perl4 プログラム。

        perl -e '$bar=q/not/; print "This is $foo{$bar} perl5"'
    
        # perl4 の出力: This is not perl5
        # perl5 の出力: This is perl5
  • Interpolation

    配列の参照についても注意する必要があります。

        print "$foo{"
    
        perl 4 の出力: {
        perl 5 の出力: syntax error
  • Interpolation

    同様に、以下のようなことも注意してください:

        $foo = "baz";
        print "\$$foo{bar}\n";
    
        # perl4 prints: $baz{bar}
        # perl5 の出力: $

    perl5 は存在しない $foo{bar} を探しに行きますが、perl4 は $foo を“baz”に展開しただけで満足します。 eval でもこの事に注意してください。

  • Interpolation

    evalqq() された文字列を渡した場合

        eval qq(
            foreach \$y (keys %\$x\) {
                \$count++;
            }
        );
    
        # perl4 runs this ok
        # perl5 prints: Can't find string terminator ")"

DBM Traps

General DBM traps.

DBMに関する一般的な罠。

  • DBM

    perl4 で(もしくは他の dbm/ndbm ツールで)作成した dbm データベースを、 perl5 の元で同じスクリプトで扱おうとすると失敗します。 perl5 の作成において、拡張 dbm の実装を tie しないときに dbmopen() がデフォルトで使用する関数のために (perl4 のときと同じ)同じ dbm/ndbm をリンクしていなければなりません。

        dbmopen (%dbm, "file", undef);
        print "ok\n";
    
        # perl4 の出力: ok
        # perl5 の出力: ok (IFF linked with -ldbm or -lndbm)
  • DBM

    key/value のサイズに関する制限を超えたときに発生するエラーにより、 perl5 では即座にプログラムから exit します。

        dbmopen(DB, "testdb",0600) || die "couldn't open db! $!";
        $DB{'trap'} = "x" x 1024;  # ほとんどの dbm/ndbmには大きすぎる値
        print "YUP\n";
    
        # perl4 の出力
        dbm store returned -1, errno 28, key "trap" at - line 3.
        YUP
    
        # perl5 の出力
        dbm store returned -1, errno 28, key "trap" at - line 3.

未分類の罠

Everything else.

その他全て。

  • require/do の戻り値を使ったときの罠

    doit.pl というファイルが

        sub foo {
            $rc = do "./do.pl";
            return 8;
        }
        print &foo, "\n";

    であって、do.pl が以下のような内容だったとすると、

        return 3;

    doit.pl の実行結果はこうなります。

        # perl 4 の出力: 3 (サブルーチンからすぐに抜けます)
        # perl 5 の出力: 8

    dorequire に置き換えても同じ振る舞いとなります。

  • 空文字列に LIMIT を指定して split を使った場合

        $string = '';
        @list = split(/foo/, $string, 2)

    Perl4 は空文字列を持った一要素のリストを返しますが、Perl5 は 空リストを返します。

As always, if any of these are ever officially declared as bugs, they'll be fixed and removed.

いつものように、バグとして公式に宣言されたものがあれば、 それは修正されて取り除かれるでしょう。