Perl日記

日々の知ったことのメモなどです。Perlは最近やってないです。

UNITCHECKと他復習

Perl5.10からBEGIN、CHECK、INIT、ENDの特別ブロックにUNITCHECKが追加されていたのを昨日知ったのでメモ。
あとそのほかまとめ復習。


perlmodにあったのを並び替えてみた。

#!/usr/bin/env perl
# use 5.010;

# begincheck
BEGIN { print " 1. BEGIN blocks run FIFO during compilation.\n" }
BEGIN { print " 2.   So this line comes out second.\n" }
UNITCHECK {
  print       " 4.   And therefore before any CHECK blocks.\n"
}
UNITCHECK {
  print       " 3. UNITCHECK blocks run LIFO after each file is compiled.\n"
}
CHECK { print " 6.   So this is the sixth line.\n" }
CHECK { print " 5. CHECK blocks run LIFO after all compilation.\n" }
INIT { print  " 7. INIT blocks run FIFO just before runtime.\n" }
INIT { print  " 8.   Run this again, using Perl's -c switch.\n" }
INIT { print  " 9.   You'll see the difference right away.\n" }
print         "10. Ordinary code runs at runtime.\n";
print         "11.   It runs in order, of course.\n";
print         "12.   This is anti-obfuscated code.\n";
print         "13.   It merely _looks_ like it should be confusing.\n";
END { print   "16.   So this is the end of the tale.\n" }
END { print   "15.   Read perlmod for the rest of the story.\n" }
END { print   "14. END blocks run LIFO at quitting time.\n" }

__END__
名称 実行順序
BEGIN FIFO
UNITCHECK LIFO
CHECK LIFO
INIT FIFO
END LIFO

FIFO…First in, First out…出てきたコードブロック順に実行
LIFO…Last in, First out…出てきたコードブロックの逆順に実行


UNITCHECKはBEGINとCHECKの間で実行される。
ただし、CHECKのように出てきたブロックを「あとで全部まとめて」実行するのではなくて、「package単位で」実行する


挙動検証。
ついでに他のものも含めてやってみた。
useしたpackageのnoraml codeがCHECKよりも先にきている点が興味深い。

Hoge.pm
package Hoge;
BEGIN { print "BEGIN [Hoge]\n" }
UNITCHECK { print "2. UNITCHECK [Hoge]\n" }
UNITCHECK { print "1. UNITCHECK [Hoge]\n" }
CHECK { print "2. CHECK [Hoge]\n" }
CHECK { print "1. CHECK [Hoge]\n" }
INIT { print "INIT [Hoge]\n" }
print "normal code [Hoge]\n";
END { print "2. END [Hoge]\n" }
END { print "1. END [Hoge]\n" }
1;
Fuga.pm
package Fuga;
BEGIN { print "BEGIN [Fuga]\n" }
UNITCHECK { print "2. UNITCHECK [Fuga]\n" }
UNITCHECK { print "1. UNITCHECK [Fuga]\n" }
CHECK { print "2. CHECK [Fuga]\n" }
CHECK { print "1. CHECK [Fuga]\n" }
INIT { print "INIT [Fuga]\n" }
print "normal code [Fuga]\n";
END { print "2. END [Fuga]\n" }
END { print "1. END [Fuga]\n" }
1;
#!/usr/bin/env perl

use Hoge;
use Fuga;

package main;
BEGIN { print "BEGIN [main]\n" }
UNITCHECK { print "2. UNITCHECK [main]\n" }
UNITCHECK { print "1. UNITCHECK [main]\n" }
CHECK { print "2. CHECK [main]\n" }
CHECK { print "1. CHECK [main]\n" }
INIT { print "INIT [main]\n" }
print "normal code [main]\n";
END { print "2. END [main]\n" }
END { print "1. END [main]\n" }

実行。

BEGIN [Hoge]        #
1. UNITCHECK [Hoge] # use Hoge;
2. UNITCHECK [Hoge] #
normal code [Hoge]  #
BEGIN [Fuga]          #
1. UNITCHECK [Fuga]   # use Fuga;
2. UNITCHECK [Fuga]   #
normal code [Fuga]    #
BEGIN [main]
1. UNITCHECK [main]
2. UNITCHECK [main]
1. CHECK [main]
2. CHECK [main]
1. CHECK [Fuga]
2. CHECK [Fuga]
1. CHECK [Hoge]
2. CHECK [Hoge]
INIT [Hoge]
INIT [Fuga]
INIT [main]
normal code [main]
1. END [main]
2. END [main]
1. END [Fuga]
2. END [Fuga]
1. END [Hoge]
2. END [Hoge]

BEGINとUNITCHECKがuseしたpackageごとに実行され、そのあとにCHECKを見つけた逆順に実行されている。
useしたpackageのUNITCHECK実行後に地の文(もとい地のコードか)が実行される。


まあ正直UNITCHECKをどう有効に使うのかまったくわかりませんけれども…!

コンパイルフェーズと実行フェーズ

構文解析とか字句解析とかするコンパイルフェーズと、実際にインタプリタで実行する実行フェーズはどう動くのか。
さっきのを-cオプションつけて実行してみるとコンパイルフェーズの実行結果だけ確認できる。

$ perl -c special_sub.pl
BEGIN [Hoge]
1. UNITCHECK [Hoge]
2. UNITCHECK [Hoge]
normal code [Hoge]
BEGIN [Fuga]
1. UNITCHECK [Fuga]
2. UNITCHECK [Fuga]
normal code [Fuga]
BEGIN [main]
1. UNITCHECK [main]
2. UNITCHECK [main]
1. CHECK [main]
2. CHECK [main]
1. CHECK [Fuga]
2. CHECK [Fuga]
1. CHECK [Hoge]
2. CHECK [Hoge]
special_sub.pl syntax OK

BEGIN、UNITCHECK、CHECKまでが実行される。(あとuseしたpackageの地の文も!)
逆に、出てこないINITは実行フェーズのブロックであることがわかる。

eval時

BEGINなどを含むコードをevalさせた時は、BEGINとUNITCHECKとENDしか実行されない。
BEGINは仕方ないにせよ、ENDがevalされたときは、ちゃんとそのプログラムのEND群にstackされる。

print "1. NORMAL CODE\n";

eval qq(BEGIN { print "begin\n" });
eval qq(UNITCHECK { print "unitcheck\n" });
eval qq(CHECK { print "check\n" });
eval qq(INIT  { print "init\n" });
eval qq(END   { print "end\n" });

print "2. NORMAL CODE\n";

END { print "END\n" }
1. NORMAL CODE
begin
unitcheck
2. NORMAL CODE
end  # <- ちゃんとENDになっている
END