perltrap - 不注意による Perl の罠
最も大きな罠とは、use warnings
あるいは -w スイッチを使うのを
忘れてしまうということです; perllexwarn と perlrun を
参照してください。
二番目に大きな罠とは、あなたのプログラム全体を use strict
の元で
実行しないということです。
三番目の罠は、このバージョンの Perl での変更点を読まないということです;
perldelta を参照してください。
awk に慣れた方は、以下のようなことに特に注意してください:
Perl のプログラムは、1 度だけ実行されます; 入力行毎ではありません。
-n
や -p
を使って暗黙のループを使えます。
Englishモジュールを
use English;
のようにしてロードすれば、awk でそうであったように
($/
のような)特殊変数を($RS のような)名前で参照することができます。
詳しくは perlvar を参照してください。
Perlでは、すべての単純文(simple statement)の末尾にセミコロンが必要です (ブロックの最後に置かれたときを除きます)。 改行は文の区切りとはなりません。
if
や while
では中かっこが必要です。
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/
が、その 3 番目のスラッシュが
除算演算子と解釈されてしまうので正しく解析できなくなります。
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 と C++ のプログラマは以下のことに注意すべきです:
if
や while
には中かっこが必要です。
else if
ではなく、elsif
を使わなければなりません。
C の break
と continue
は、Perl ではそれぞれ last
と
next
となります。
C とは異なり、これらは do { } while
構文では 使えません。
Loop Control in perlsyn を参照してください。
switch 文は given/when
と呼ばれ、perl 5.10 以降でのみ利用可能です。
Switch statements in perlsyn を参照してください。
Perl では、変数は“$”か“@”か“%”で始まります。
コメントの始まりは、“#”であり、“/*”や "//" ではありません。 Perl は C/C++ のコメントを除算演算子、終端していない正規表現、 定義性和演算子として解釈するかもしれません。
なにかのアドレスを得ることはできません。 Perl には似たような演算子であるバックスラッシュがありますが、これは リファレンスを生成します。
ARGV
は大文字でなければなりません。
$ARGV[0]
が C での argv[1]
に相当し、argv[0]
にあたるものは
$0
です。
link()
, unlink()
, rename()
などのシステムコールは、成功時に
0 ではなく非 0 の値を返します。
(但し、system() は成功時に 0 を返します。)
シグナルハンドラは、シグナル番号ではなくシグナル名を扱います。 使用できるシグナル名は、kill -l として確かめてください。
熟練した sed プログラマは以下のことに注意すべきです:
Perl のプログラムは、1 度だけ実行されます; 入力行毎ではありません。
-n
や -p
を使って暗黙のループを使えます。
置換における後方参照には、“\”ではなく“$”を使います。
"(", ")", "|" といったパターンマッチのメタキャラクタは、その直前に バックスラッシュを置く必要はありません。
範囲演算子は ...
であって、カンマではありません。
鋭いシェルプログラマは以下のことに注意すべきです:
バッククォート演算子は、コマンド内にシングルクォートがあっても 変数の展開を行ないます。
バッククォート演算子は csh とは違って、返された値を変換しません。
シェル (特に csh) は、コマンドラインごとに何段階もの置換を行ないます。 Perl はダブルクォート、バッククォート、山かっこ、検索パターンといった 特定の構造でだけ置換を行ないます。
シェルは一度に少しずつ解釈を行ないます。
Perl は実行前にプログラム全体をコンパイルします
(コンパイル時に実行される BEGIN
ブロックを除く)。
引数は $1, $2 などではなく、@ARGV から得られます。
環境変数は、自動的には独立したスカラ変数として利用できるように なりません。
シェルの test
は "=", "!=", "<" などを文字列比較に使い、"-eq", "-ne",
"-lt" などを数値比較に使います。
これは Perl とは逆です; Perl では eq
, ne
, lt
を文字列比較に使い、
==
, !=
<
などを数値比較に使います。
実践的な Perl プログラマは以下のことに注意すべきです:
多くの演算子がリストコンテキストとスカラコンテキストとで 振る舞いが変わることを忘れないでください。 詳しくは perldata を参照してください。
裸の単語、特に全てが小文字のものはできる限り使わないでください。 見た目だけではその「裸の単語」が関数なのか、 文字列なのかが判断できません。文字列にはクォートを、 関数呼び出しには括弧をつければ、迷うこともないでしょう。
組込み関数のどれが(chop() や chdir())のような単項演算子で、 どれが(print() や unlink())のような リスト演算子であるかは見ただけではわかりません (プロトタイプがなければ、ユーザー定義サブルーチンは リスト演算子として のみ 定義でき、単項演算子にはできません)。 perlop と perlsub を参照してください。
いくつかの関数が $_ や @ARGV などをデフォルトにしていますが、 同じことを期待する他の関数がデフォルトになっていないことを覚えるのに、 辛いタイピングが必要でしょう。
<FH> 構造はファイルハンドルではなく、そのハンドルに対する行読み込みの 操作(readline operation)です。 while ループの条件式の中にこのファイル読み込みだけがあった場合には 読み込まれたデータは $_ に代入されます。
while (<FH>) { } while (defined($_ = <FH>)) { }.. <FH>; # data discarded!
=~
が必要なところで c<=> を使わない、ということを忘れないでください。
これら二つの構造はかなり違います。
$x = /foo/; $x =~ /foo/;
do {}
構造は、ループ制御を行えるような本当のループではありません。
ローカル変数は、my() で済むところではこれで済ませること
(使えない場所については、perlform を参照してください)。
local()
を使えばグローバル変数に対するローカルな値を与えますが、
動的スコープの不慮の副作用の可能性は、そのままです。
モジュールにある export された変数を局所化すると、その export された 値は変更されません。 ローカル名は新しい値の別名(alias)となりますが、 外部名は元々の値の別名のままです。
実践的な Perl4 プログラマは 以下に挙げる Perl4 と Perl5 の違いに特有な罠に気をつけた 方が良いでしょう。
以下は順不同のリストです。
修正された perl4 のバグや、なくなった perl4 の仕様、 perl5 で仕様の変わったもの。
新しい構文解析器によって引き起こされるもの。
数値や算術演算子に関する罠。
perlの標準的なデータ型に内包される罠。
リスト内のコンテキストや、スカラ文/宣言に関する罠。
構文解析、評価、コードの実行の優先順に関係した罠。
パターンマッチングの仕様に関する罠。
シグナル及びシグナルハンドラ、一般的なサブルーチン、ソート、 ソートのためのサブルーチンに関連した罠。
OS特有の罠。
dbmopen()
の使用や、dbm の実装に関連した罠。
その他の罠。
もしここで挙げたリストにはないような変換の罠の
例を見つけたら、それを <perlbug@perl.org> まで送ってください。
また、少なくともこれらのいくつかは use warnings
プラグマか
-w スイッチで捕捉できることに注意してください。
perl4 から、なくなったり修正されたことがら。
“_”で始まるシンボルはもはや main パッケージに強制的に結び付けられることは、
$_
自身(と @-
など)を除いてはなくなりました。
package test; $_legacy = 1;
package main; print "\$_legacy is ",$_legacy,"\n";
# perl4 prints: $_legacy is 1 # perl5 prints: $_legacy is
ダブルコロンは、変数名の中でパッケージセパレータになるようになりました。 このため、以下の例では perl4 と perl5 とでは振る舞いが変わります。 これはパッケージが実在してないためです。
$a=1;$b=2;$c=3;$var=4; print "$a::$b::$c "; print "$var::abc::xyz\n";
# perl4 prints: 1::2::3 4::abc::xyz # perl5 prints: 3
::
は今では、(これがバグとして分類すべきかどうかはともかく)
パッケージデリミタとしてみなされるようになっています
(ここでは古いパッケージデリミタの ' を使っています)。
$x = 10; print "x=${'x}\n";
# perl4 prints: x=10 # perl5 prints: Can't find string terminator "'" anywhere before EOF
常にパッケージ名を含めることで、この問題を避けつつ、perl4 との 互換性を維持することができます:
$x = 10; print "x=${main'x}\n";
$:
の解析については、優先順位の罠 も参照してください。
splice()
の第 2、第 3 引数はスカラコンテキストになりました
splice()
の第 2、第 3 引数は
(ラクダ本にある通り)リストコンテキストではなく
スカラコンテキストで評価されるようになりました。
sub sub1{return(0,2) } # return a 2-element list sub sub2{ return(1,2,3)} # return a 3-element list @a1 = ("a","b","c","d","e"); @a2 = splice(@a1,&sub1,&sub2); print join(' ',@a2),"\n";
# perl4 prints: a b # perl5 prints: c d e
goto
は使えません
最適化によってなくなってしまうようなブロックの中に
飛び込む 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
空白を変数の名前や、クォート構造の区切りに使うことは 構文的に正当なものではなくなりました。ちぇっ、ちぇっ。
$a = ("foo bar"); $b = q baz; print "a is $a, b is $b\n";
# perl4 prints: a is foo bar, b is baz # perl5 errors: Bareword found where operator expected
while/if BLOCK BLOCK
対応はなくなりました
古い while/if BLOCK BLOCK の構文は、もはやサポートされていません。
if { 1 } { print "True!"; } else { print "False!"; }
# perl4 prints: True! # perl5 errors: syntax error at test.pl line 1, near "if {"
**
は単項のマイナスよりも優先順位が高いです
**
演算子の優先順位は、単項のマイナスよりも高くなりました。
これは以前からドキュメントにはそうあったのですが、実際は違っていました。
print -4**2,"\n";
# perl4 prints: 16 # perl5 prints: -16
foreach
が変わりました
配列ではないリストに対する繰り返しで
あるときの foreach{}
の意味が変わりました。
以前はそういったリストはテンポラリな配列に代入されていましたが、
現在はそうではありません(効率上の理由です)。
これは、現在では値のコピーに対して繰り返しをするのではなく
実際の値に対して繰り返しをするということです。
@list = ('ab','abc','bcd','def'); foreach $var (grep(/ab/,@list)){ $var = 1; } print (join(':',@list));
# perl4 prints: ab:abc:bcd:def # perl5 prints: 1:1:bcd:def
perl4 と同じようにするには、自分で陽にテンポラリの配列へと コピーしてからその配列に対して繰り返しを行います。例えば、
foreach $var (grep(/ab/,@list)){
この部分を以下のように変更します。
foreach $var (@tmp = grep(/ab/,@list)){
そうしないと、$var を変更したときに @list の値に影響が出ます
(これはループ変数に $_
を使っていて、かつ、
$_
を局所化していないようなサブルーチンを
ループの中で呼ぶようなときに良く起こります)。
split
の振る舞いが変わりました
引数なしの split
の振る舞いは、split /\s+/
($_ が空白から始まっているときに先頭のフィールドが空になる)
$_ = ' hi mom'; print join(':', split);
# perl4 prints: :hi:mom # perl5 prints: hi:mom
perl4 では、-e スイッチにアタッチされたテキストは無視されて、常に 後に続く引数からプログラムが取り出されていました。さらに、 -e スイッチの後に引数を渡さない場合も受け付けていました。 これらの振る舞いは両方とも修正されました。
perl -e'print "attached to -e"' 'print "separate arg"'
# perl4 prints: separate arg # perl5 prints: attached to -e
perl -e
# perl4 prints: # perl5 dies: No code specified for -e.
push
は結果リストの要素数を返すようになりました
perl4 では push
の戻り値はドキュメントに書かれていませんでしたが、
実際には対象となるリストに最後にpushした値が返されていました。
Perl5 では、push
の戻り値はドキュメントに明記され、かつそれは
perl4 から変更されました。これは push した後のリストにある要素の数を
返します。
@x = ('existing'); print push(@x, 'first new', 'second new');
# perl4 prints: second new # perl5 prints: 3
一部のエラーメッセージが異なっています。
split()
はサブルーチンの引数を尊重します
Perl 4 では、リストコンテキストで split()
の最初の引数の
デリミタが ??
だった場合、返される結果が @_
にも設定されました。
Perl 5 ではサブルーチンの引数により多くの敬意を払います。
幾つかのバグがうかつにも修正されているかもしれません :-)
パースに関する Perl4 と Perl5 の違いの罠です。
. と = の間にあるスペースに注意。
$string . = "more string"; print $string;
# perl4 prints: more string # perl5 prints: syntax error at - line 1, near ". ="
perl5 では構文解析が改良されました。
sub foo {} &foo print("hello, world\n");
# perl4 prints: hello, world # perl5 prints: syntax error
“それが関数に見えるのなら、それは関数だ”のルール。
print ($foo == 1) ? "is one\n" : "is zero\n";
# perl4 prints: is zero # perl5 warns: "Useless use of a constant in void context" if using -w
$#array
の文字列展開が異なります
$#array
構造の文字列展開で名前の周りにブレースがあるときには
違いがあります。
@a = (1..3); print "${#a}";
# perl4 prints: 2 # perl5 fails with syntax error
@ = (1..3); print "$#{a}";
# perl4 prints: {a} # perl5 prints: 2
map
, grep
の後の {
が BLOCK の開始かハッシュリファレンスかを推測します
perl が map {
(または grep {
) を見つけると、{
が BLOCK の開始か
ハッシュリファレンスかを推測します。
推測に失敗すると、}
の近くでカンマがない (あるいは予想外の位置に
カンマがある) という文法エラーが報告されます。
ハッシュリファレンスの {
の前に単項の +
を、また BLOCK 内の最初の
ものに({
の後に) 単項の +
を使ってください; perl がいつでも正しく
推測できるようにするためです (map in perlfunc を参照してください)。
同じものに対する数値演算子、オペランド、出力に関する Perl4 と Perl5 の違いの罠です。
書式指定された出力と、最下位の数字。 一般的に Perl 5 はより的確に試します。 例えば、Solaris Sparc では:
print 7.373504 - 0, "\n"; printf "%20.18f\n", 7.373504 - 0;
# Perl4 prints: 7.3750399999999996141 7.375039999999999614
# Perl5 prints: 7.373504 7.375039999999999614
Perl 5 では、最初の結果がより良いものであることに注目してください。
あなたが実行した結果は違うかもしれません; あなたの浮動小数点フォーマット ルーチンや、そもそも浮動小数点フォーマットが少し違うかもしれないからです。
ここに記述されていた項目は削除されました。 以前あったものでは、オートインクリメント演算子が符号付き整数の限界を越えたことを 検知しないということを紹介していました。 これはバージョン 5.003_04 で修正されましたが、大きな整数を取り扱うことに 不安を感じているのであれば
use Math::BigInt;
数値の等価性の比較の結果を代入しても、perl ではその比較の結果が 偽 (0) であったときにはうまくいきません。 論理比較は、現在 0 ではなく null を返します。
$p = ($test == 1); print $p,"\n";
# perl4 prints: 0 # perl5 prints:
この新しい仕様の別の例は s/// などを使ったときの一般的な正規表現の罠 を参照してください。
数値としても文字列としても操作可能なビット操作演算子 (& | ^ ~
) が
文字列のみを引数として与えられた場合、
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 と Perl5 の違いの罠です。
配列の添え字が負であったとき、それは配列の終端から数えられるように なりました。
@a = (1, 2, 3, 4, 5); print "The third element of the array is $a[3] also expressed as $a[-2] \n";
# perl4 prints: The third element of the array is 4 also expressed as # perl5 prints: The third element of the array is 4 also expressed as 4
$#array
により小さな値を設定すると配列要素が捨てられるようになりました
$#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 prints: Before: abcde, After: ab, Recovered: abcd # perl5 prints: 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 prints: # perl5 dies: hash %h defined
Perl は defined(@a)
と defined(%h)
に警告を出すようになりました。
ある変数から別の変数へのグロブの代入は、 代入された変数が代入の後で局所化されているときには 失敗します。
@a = ("This is Perl 4"); *b = *a; local(@a); print @b,"\n";
# perl4 prints: This is Perl 4 # perl5 prints:
undef
のグロブへの代入
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 prints: aab : -0 : 1 # perl5 prints: 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
defined $var
の振る舞いは変わりました
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"; # what should print $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
フォーマットに対する引数リストの要素は、リストコンテキストで 評価されるようになりました。 これは、今やリストの値が 展開できるようになったということです。
@fmt = ("foo","bar","baz"); format STDOUT= @<<<<< @||||| @>>>>> @fmt; . write;
# perl4 errors: Please use commas to separate fields in file # perl5 prints: foo bar baz
caller()
関数は呼び出し元がなくスカラコンテキストで呼ばれた場合には偽を返します
caller()
関数は呼び出し元がなく、スカラコンテキストで呼ばれた
場合には偽を返すようになりました。
これによりライブラリファイルが
(自分が)require されたのかを判断することができます。
caller() ? (print "You rang?\n") : (print "Got a 0\n");
# perl4 errors: There is no caller # perl5 prints: 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
sprintf()
のプロトタイプは ($;@)
sprintf()
のプロトタイプは ($;@) なので、最初の引数は
スカラコンテキストです。従って、配列を渡すと、
Perl 4 とは違っておそらくあなたの望まない結果になるでしょう。
@z = ('%s%s', 'foo', 'bar'); $x = sprintf(@z); print $x;
# perl4 prints: foobar # perl5 prints: 3
しかし、printf()
のほうは Perl 4 と同じように動きます:
@z = ('%s%s', 'foo', 'bar'); printf STDOUT (@z);
# perl4 prints: foobar # perl5 prints: foobar
評価順序に関する Perl4 と Perl5 の違いの罠です。
Perl4 はほとんどの演算子で Perl5 と同じ優先順位を持っています。 しかし Perl4 では、ドキュメントとは少々異なるような 一貫性に欠けるものがあります。
左辺と右辺は両方とも演算子を取るようになりました。 LHS は perl4 では最初に、perl5 では 2 番目に評価されます; これは 部分式の副作用の関係に影響を与えることがあります。
@arr = ( 'left', 'right' ); $a{shift @arr} = shift @arr; print join( ' ', keys %a );
# perl4 prints: left # perl5 prints: right
以下の例は、優先順位の関係で意味エラー(semantic error)となるようになりました。
@list = (1,2,3,4,5); %map = ("a",1,"b",2,"c",3,"d",4); $n = shift @list + 2; # first item in list plus 2 print "n is $n, "; $m = keys %map + 2; # number of items in hash plus 2 print "m is $m\n";
# perl4 prints: n is 3, m is 6 # perl5 errors and fails to compile
代入演算子の優先順位は、代入と同じとなりました。 perl4 は間違って、この優先順位が関連演算子と同じものに なっています。このため、式の中にあるのと同じように括弧でくくらなければ ならなくなりました。
/foo/ ? ($a += 2) : ($a -= 2);
下のようにしてしまうと、
/foo/ ? $a += 2 : $a -= 2
これは
(/foo/ ? $a += 2 : $a) -= 2;
のように間違って解析されてしまいます。 その一方で、
$a += /foo/ ? 1 : 2;
これは C プログラマーが期待するであろう動作になりました。
open
はファイルハンドルの周りにかっこが必要です
open FOO || die;
これはもはや正しくなくなりました。ここではファイルハンドルを 括弧で括る必要があります。さもなければ、perl5 はこういった文を そのデフォルトの優先順位のまま放っておきます(以下の例参照)。
open(FOO || die);
# perl4 opens or dies # perl5 opens FOO, dying only if 'FOO' is false, i.e. never
$:
は $::
と同じ優先順位
perl4 は特殊変数 $:
の優先順位に、perl5 が $::
を main パッケージと
みなすのと同じ優先順位を与えます。
$a = "x"; print "$::a";
# perl 4 prints: -:a # perl 5 prints: x
perl には、ファイルテスト演算子と代入演算子を組み合わせたときに
優先順位のバグがありました。したがって、Perl4 の優先順位テーブルでは
-e $foo .= "q"
は
((-e $foo) .= "q")
と解釈すべきなのに実際には
((-e $foo) .= "q")
と解釈していました。
Perl5 ではドキュメントにある通りの優先順位です。
-e $foo .= "q"
# perl4 prints: no output # perl5 prints: Can't modify -e in concatenation
keys
, each
, values
は通常の名前付き単項演算子
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'$lhs'$rhs'
interpolates on either side
s'$lhs'$rhs'
はもはやいずれの辺にあっても展開されなくなりました。
以前は $lhs は展開し、$rhs を展開しませんでした。
(文字列にあるリテラルの '$' にはいまでもマッチしません)。
$a=1;$b=2; $string = '1 2 $a $b'; $string =~ s'$a'$b'; print $string,"\n";
# perl4 prints: $b 2 $a $b # perl5 prints: 1 2 $a $b
m//g
attaches its state to the searched string
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 prints: infinite loop blah...
m//o
used within an anonymous sub
現在のところ、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";
Perl5 のほとんどのビルドでは、これは以下のように出力します:
ok not ok not ok
この例の場合、build_match() は常に最初
に build_match()
が呼ばれたときの
$left と $right の内容にマッチするようなサブルーチンを返します。
呼び出されたその時点での値ではありません。
$+
isn't set to whole match
マッチングの中で括弧が使われなかった場合、perl4 では
$+
には $&
と同じようにマッチした全体がセットされますが、
Perl5 ではそうではありません。
"abcdef" =~ /b.*e/; print "\$+ = $+\n";
# perl4 prints: bcde # perl5 prints:
置換は、失敗したときには空文字列を返すようになりました。
$string = "test"; $value = ($string =~ s/foo//); print $value, "\n";
# perl4 prints: 0 # perl5 prints:
この新しい仕様に関しては 数値の罠 も参照してください。
s`lhs`rhs`
is now a normal substitution
s`lhs`rhs`
(バッククォートの使用)は通常の置換となり、
バッククォートの展開は行われなくなりました。
$string = ""; $string =~ s`^`hostname`; print $string, "\n";
# perl4 prints: <the local hostname> # perl5 prints: hostname
正規表現中の変数の使用に関する構文解析がより厳密になりました。
s/^([^$grpc]*$grpc[$opt$plus$rep]?)//o;
# perl4: compiles w/o error # perl5: with Scalar found where operator expected ..., near "$opt$plus"
同じスクリプトでこの例に付け加えことは、置換後の文字列の実際の値です。
[$opt]
は perl4 ではキャラクタクラスであり、perl5 では配列の
添え字となります。
$grpc = 'a'; $opt = 'r'; $_ = 'bar'; s/^([^$grpc]*$grpc[$opt]?)/foo/; print;
# perl4 prints: foo # perl5 prints: foobar
m?x?
matches only once
perl5 では、m?x?
は ?x?
と同様に一回だけマッチします。
perl4 では、/x/
や m!x!
と同じように何度でもマッチします。
$test = "once"; sub match { $test =~ m?once?; } &match(); if( &match() ) { # m?x? matches more then once print "perl4\n"; } else { # m?x? matches only once print "perl5\n"; }
# perl4 prints: perl4 # perl5 prints: perl5
Ruby と違って、Perl ではマッチングに失敗してもマッチング変数
($1, $2, ..., $`
, ...) はリセットされません。
Perl4 と Perl5 の違いの罠に分類される一般的なものは シグナル、ソート、そしてそれらに関連する サブルーチンで、いくつかの OS 固有の罠を含めた サブルーチンの罠と同じ様なものです。
文字列のように見える裸の単語は、その名前が使うよりも前に サブルーチンの名前として定義されている場合にはサブルーチンとして みなされます。
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 を使って、これを発見できます。
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 prints: Foo! # perl5 prints: String found where operator expected
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...
seek()
appends correctly
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 と Perl5 の違いの罠には、式や文、コンテキストなどにおける 展開に関するものがあります。
@
always interpolates an array in double-quotish strings
ダブルクォートで囲まれた文字列にある @ は常に配列に展開されます。
print "To: someone@somewhere.com\n";
# perl4 prints: To:someone@somewhere.com # 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
ダブルクォートで括られた文字列がエスケープされていない $ で 終了することがなくなりました。
$foo = "foo$"; print "foo is $foo\n";
# perl4 prints: foo is foo$ # perl5 errors: Final $ should be \$ or $name
注意: perl5 は、$bar の終端にある @ については“エラーにしません”
Perl はダブルクォート中にあるブレースの内側の任意の式を
評価するようになりました(一般的には、$
や @
に続いて
開きのブレースがきたときです)。
@www = "buz"; $foo = "foo"; $bar = "bar"; sub foo { return "bar" }; print "|@{w.w.w}|${main'foo}|";
# perl4 prints: |@{w.w.w}|foo| # perl5 prints: |buz|bar|
use strict;
を使って、perl5 におけるこのような罠を避けることが
できることに注意してください。
$$x
now tries to dereference $x
"this is $$x" はプロセス ID を展開するようになっていましたが、
今は $x の参照外し(dereference)を試みます。
それでも $$
自身は今でもきちんと動作します。
$s = "a reference"; $x = *s; print "this is $$x\n";
# perl4 prints: this is XXXx (XXX is the current pid) # perl5 prints: this is a reference
eval "EXPR"
requires protection
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 prints: Yup # perl5 prints: Nope
以下のものを:
eval "\$$hashname{'$key'} = q|$value|";
次のように変更すると:
eval "\$\$hashname{'$key'} = q|$value|";
結果はこうなります:
# perl4 prints: Nope # perl5 prints: Yup
以下のように変更した場合には
eval "\$$hashname\{'$key'\} = q|$value|";
結果はこうなります。
# perl4 prints: Yup # perl5 prints: Yup # and is compatible for both versions
以前のバージョンにあったバグに依存しているような perl4 プログラム。
perl -e '$bar=q/not/; print "This is $foo{$bar} perl5"'
# perl4 prints: This is not perl5 # perl5 prints: This is perl5
展開中の配列とハッシュの大かっこについても注意する必要があります。
print "$foo["
perl 4 prints: [ perl 5 prints: syntax error
print "$foo{"
perl 4 prints: { perl 5 prints: syntax error
Perl 5 は、個々のかっこには添え字かキー名が続き、さらに適切な種類の 閉じかっこが続くと仮定しています。 Perl 4 の振る舞いを模倣するためには、そのようなかっこを エスケープしなければなりません。
print "$foo\["; print "$foo\{";
\$$foo{bar}
同様に、\$$foo{bar}
にも注意してください:
$foo = "baz"; print "\$$foo{bar}\n";
# perl4 prints: $baz{bar} # perl5 prints: $
perl5 は存在しない $foo{bar}
を探しに行きますが、perl4 は
$foo を“baz”に展開しただけで満足します。
eval
でもこの事に注意してください。
qq()
string passed to eval
will not find string terminator
eval
に qq()
された文字列を渡した場合
eval qq( foreach \$y (keys %\$x\) { \$count++; } );
# perl4 runs this ok # perl5 prints: Can't find string terminator ")"
DBMに関する一般的な罠。
dbmopen()
perl4 で(もしくは他の dbm/ndbm ツールで)作成した dbm データベースを、
perl5 の元で同じスクリプトで扱おうとすると失敗します。
perl5 の作成において、拡張 dbm の実装を tie
しないときに
dbmopen()
がデフォルトで使用する関数のために
(perl4 のときと同じ)同じ dbm/ndbm をリンクしていなければなりません。
dbmopen (%dbm, "file", undef); print "ok\n";
# perl4 prints: ok # perl5 prints: ok (IFF linked with -ldbm or -lndbm)
perl4 (またはその他の dbm/ndbm ツール)で作成された dbm は、perl5 で このスクリプトの実行に失敗することがあります。 key/value のサイズに関する制限を超えたときに発生するエラーにより、 perl5 では即座にプログラムから exit します。
dbmopen(DB, "testdb",0600) || die "couldn't open db! $!"; $DB{'trap'} = "x" x 1024; # value too large for most dbm/ndbm print "YUP\n";
# perl4 prints: dbm store returned -1, errno 28, key "trap" at - line 3. YUP
# perl5 prints: dbm store returned -1, errno 28, key "trap" at - line 3.
その他全て。
require
/do
の戻り値を使ったときの罠
doit.pl というファイルが
sub foo { $rc = do "./do.pl"; return 8; } print &foo, "\n";
であって、do.pl が以下のような内容だったとすると、
return 3;
doit.pl の実行結果はこうなります。
# perl 4 prints: 3 (aborts the subroutine early) # perl 5 prints: 8
do
を require
に置き換えても同じ振る舞いとなります。
split
を使った場合
$string = ''; @list = split(/foo/, $string, 2)
Perl4 は空文字列を持った一要素のリストを返しますが、Perl5 は 空リストを返します。
いつものように、バグとして公式に宣言されたものがあれば、 それは修正されて取り除かれるでしょう。