と、それは置いておいて、Perlでconstant folding・定数とされるのは文字列・数値リテラルか、定数扱いできる関数だけです。
定数扱いできる関数というのは実は決まっていて、以下の条件がそろわないといけない:
- その関数は 引数を取らない、とprototypeで明示的に宣言してある
- その関数は文字列・数値リテラルを返し、それ以外の処理を行わない
こちらのコードを実行してみるとわかりやすい
use strict; use O ('Deparse'); sub not_const { 1 } sub const_num() { 1 } sub const_string() { "foo" } sub const_list() { [ qw(1 2 3) ] } sub const_hash() { { a => 1, b => 2, c => 3 } } if (not_const) { print "This statement was not folded\n"; } if (const_num) { print "This statement was not folded\n"; } if (const_string) { print "This statement was not folded\n"; } if (const_list) { print "This statement was not folded\n"; } if (const_hash) { print "This statement was not folded\n"; }
このコードを実行すると、perl -MO=Deparseされた状態のソースコードが表示される。全ての関数はなんらかの真の値を返すので、constant foldingされる箇所はないのだが、全てが真の値なため可能なところでは if () 文が省略されているのがわかると思う。すなわち、if() 文が残っている部分は定数として解釈されなかったわけだ。
筆者の環境(perl-5.16.1)では以下のような出力がされています:
sub const_num () { 1 } sub const_string () { 'foo' } sub not_const { use strict; 1; } sub const_list () { use strict; ['1', '2', '3']; } sub const_hash () { use strict; +{'a', 1, 'b', 2, 'c', 3}; } use strict; if (not_const ) { print "This statement was not folded\n"; } do { print "This statement was not folded\n" }; do { print "This statement was not folded\n" }; if (const_list) { print "This statement was not folded\n"; } if (const_hash) { print "This statement was not folded\n"; }
これから見てわかるのは not_const, const_list, const_hashに関してはif文が残っている。定数扱いされてなーい!
ということはリテラルではない値を定数扱いできないのか?というと、そうではない。constant プラグマを使えばオッケー!
さきほどのスクリプトを以下のように変更すると差が見えます。
use strict; use O ('Deparse'); sub not_const { 1 } sub const_num() { 1 } sub const_string() { "foo" } sub const_list() { [ qw(1 2 3) ] } sub const_hash() { { a => 1, b => 2, c => 3 } } use constant const_list_with_pragma => [ qw(1 2 3) ]; use constant const_hash_with_pragma => { a => 1, b => 2, c => 3 }; if (not_const) { print "This statement was not folded\n"; } if (const_num) { print "This statement was not folded\n"; } if (const_string) { print "This statement was not folded\n"; } if (const_list) { print "This statement was not folded\n"; } if (const_hash) { print "This statement was not folded\n"; } if (const_list_with_pragma) { print "This statement was not folded\n"; } if (const_hash_with_pragma) { print "This statement was not folded\n"; }
今度は以下のように出力されるはず
sub const_num () { 1 } sub const_string () { 'foo' } sub not_const { use strict; 1; } sub const_list () { use strict; ['1', '2', '3']; } sub const_hash () { use strict; +{'a', 1, 'b', 2, 'c', 3}; } use constant ('const_list_with_pragma', ['1', '2', '3']); use constant ('const_hash_with_pragma', {'a', 1, 'b', 2, 'c', 3}); use strict; if (not_const ) { print "This statement was not folded\n"; } do { print "This statement was not folded\n" }; do { print "This statement was not folded\n" }; if (const_list) { print "This statement was not folded\n"; } if (const_hash) { print "This statement was not folded\n"; } do { print "This statement was not folded\n" }; do { print "This statement was not folded\n" };
というわけで、リテラル以外の値を定数にしたい場合はuse constantしましょう。(もしくはXSからnewCONSTSUBで定義する、という裏技もあります)