うーん、そうなのだけど....
メタプログラミングとは - Perl入門〜サンプルコードによるPerl入門〜メタプログラミングとはソースコードを生成するプログラミングのことです。メタプログラミングによって生成したソースコードは、eval関数で実行することができます。
evalだけがメタプログラミングの技法ではないし、またevalはその威力ゆえ最後の選択肢とすべきだ。
#!/usr/local/bin/perl
use strict;
use warnings;
use Benchmark qw/timethese cmpthese/;
cmpthese(
timethese(
0,
{
eval => sub {
no warnings 'redefine'; # comment this out leter
my $src = 'sub add{ $_[0] + $_[1] }';
eval $src;
add( 1, 1 ) == 2 or die;
},
subref => sub {
my $add = sub { $_[0] + $_[1] };
$add->( 1, 1 ) == 2 or die;
}
}
)
);
これを手元で実行してみると、以下のような結果となる。
Rate eval subref
eval 33283/s -- -97%
subref 1021681/s 2970% --
こんな単純な例でも、30倍の速度差が出ている。
それより深刻なのは、evalは実行中のソースコードを書き換えてしまう危険があるということ。上のコードのno warnings 'redefine';を実行してみてほしい。
Subroutine add redefined at (eval 6) line 1. Subroutine add redefined at (eval 7) line 1. Subroutine add redefined at (eval 8) line 1. ...
というエラーメッセージが延々と出るはずだ。もし本当にaddというサブルーティンが定義されていたとしたら、元のコードが破壊されてしまう。
「evalは最後の武器」というのは、Perlに限らずメタプログラミング可能な言語における鉄則とも言える。たいていの場合、無名関数を生成するだけで同様の目的をより安全かつ高速に行えるのだ。
ちょっと面白いのが、JavaScriptの場合、文字列からfunctionを生成するのに、eval()だけではなくnew Function()を使う手がある。
eval
var fun;
eval('fun = function(a,b){ return a + b }');
if (fun(1,1) !== 2) alert('whoa!');
new Function
var fun = new Function('a','b','return a + b');
if (fun(1,1) !== 2) alert('whoa!');
function object
var fun = function(a,b){ return a + b };
if (fun(1,1) !== 2) alert('whoa!');
Firefox 3, Safari 3, IE7 ではnew Function()の方が高速なのだが、Opera 9.63とChromeではeval()の方が高速だった。いずれの場合も、差は3割以上と有意だった。
この場合でも、普通にfunctionオブジェクトを生成するのが一番高速なのは全ブラウザー共通であることは想定の範囲内。
大事なことなので繰り返す。メタプログラミングにおいて、
- eval()は最後の武器
- それしかない場合に用いよ
- たいてい、無名関数で事足りる
Dan the Metaprogrammer
原因と結果が緩やかにしか結びつかないということはありそうです。
特に「意味」の世界は「最小作用の原理」とか「フェルマーの原理」
が成り立つような、ギチギチな構造をしていないように見えます。
特定の機能を実現せず、かえって人間に負担を与えるような「慣習」が
ずっと残り続けるということはありそうだと思えてしまいます。
だから、三角も、「やじり」のようなものから生まれたかも知れません。
下(底辺)がっちり先(頂点)とんがりの構造は武器として凄いぞ!
といった「実感」といえるようなものから生まれたかもしれません。
丸は太陽から、とかだと学問的、神話的過ぎるような気がしてしまいます。
よく転がるぞ!という「実感」がきっかけだったと考えたいです。
半円はご飯を盛る器とか(半球?)ブーメランとか?