鋭い質問です。
Perlの謎(その10)サブルーチンの呼び出し方 - 燈明日記組み込み関数と同名のユーザ定義関数を定義したときは、どうしても『&』付きでないと呼び出せないのです。
id:chaichanPaPaの主張は、以下のとおり確認できます。
#!/usr/bin/perl use strict; use warnings; sub atan2{ "atanatan"; } print atan2(1,1), "\n";
しかし、実際にはビルトイン関数を上書きしているモジュールは少なくありません。たとえばCGI::Carpはdie()
やwarn()
を上書きしています。
それでは、ビルトイン関数の上書きはどうやるのでしょうか?そして、一旦上書きされたビルトイン関数を呼び出すには一体どうすればいいのでしょうか。
こうすればよいのです。
#!/usr/bin/perl use strict; use warnings; BEGIN{ *CORE::GLOBAL::atan2 = sub{ "atanatan"; }; } print atan2(1,1), "\n"; print CORE::atan2(1,1), "\n";
BEGIN{}
でくくられていることに注目して下さい。これがないとうまく行きません。
Perlでは、ほとんどの外部モジュールは、require
ではなくuse
されます。use Foo;
は
BEGIN{ require Foo; Foo->import(); }
と等価ですから、そこで*CORE::GLOBAL::atan2
を定義しておけば、ビルトイン関数といえど上書きできるわけです。そして、CORE::atan2
を使えば、元のビルトイン関数にはいつでもどこでもアクセスできます。
また、現在、組み込み関数と同名がないユーザ定義関数を作って『&』無しで呼んで上手くいっていても、将来、同名の組み込み関数が出来る可能性もあるわけです。
この主張には一理ありますが、三つの理由で&
はつかうべきではありません。
- ビルトイン関数は滅多なことでは増えない
Perl 5の最大の特徴は、モジュールを使っていくらでも機能拡張できることにあります。逆に言えば、Perl本体にそれ以上ビルトイン関数を増やすインセンティブは低いのです。このあたりは「まだ若い」JavaScriptや、ビルトイン関数の多さが機能の多さだというユーザーが多いPHPとは違うところです。
それでも
say
のように「あまりに人気があったので」組み込みになったものや、given
のように構文を変えるために付け加えるものがごくたまにあるのは事実です。が、こうした機能追加はまずモジュールで実装され、十分な議論を経てから追加されるので、ある日いきなりある単語がビルトイン関数名になっているということはまずありえません。むしろ、Perl 4の負の遺産として、ビルトイン関数が多すぎるというのがモダンPerlモンガーの感想ではないでしょうか。
- ビルトイン関数を上書きする方法がすでに用意されている
前述のとおりです。
dump()
やdbmopen()
など、今では「虫垂化」しているビルトイン関数も少なくないのですが、それでもほとんどのビルトイン関数は、必然性があってそうなっているわけです。滅多なことで上書きするべきではありませんし、そして上書きするのであれば確信犯的に、自分が何をしているか知った上でやるべきです。&atan2
とatan2
で区別するというのは上策とは言えません。 - 知らないでビルトイン関数と同名の関数を使ったことを教えてくれなくなる
Perlという言語は、プログラマーの意思の強さを明示性で推し量る言語です。自然言語も含め、どの言語も多かれ少なかれそうなのですが、Perlはとくにその傾向が強い。
&
を頭につけるということは、「Perlよ、俺は自分のやっていることがわかってるんだからビルトインがあろうがなかろうが俺の言う通りやれ」とPerlに命じることになります。この場合、Perlは折角の警告をひっこめてしまいます。DWIS (Do what I say) は DWIM (Do what I mean)に比べると、プログラマーの責任が大きくなります。
それでも、&
が必要な場面が一つだけ残っています。それは、サブルーチンのレファレンスを引数として渡すとき。たとえば、こんな感じ。
#!/usr/bin/perl use strict; use warnings; use Encode; use utf8; my $utf8 = '堕落'; sub fallback{ sprintf "\\x{%x}", shift; } print encode('ascii', $utf8, \&fallback), "\n";
このあたりの詳しいことは、「実用Perlプログラミング」の第一章に詳しく乗っています。お勧めです。
Dan the Perl Monger
From http://www.perlfoundation.org/perl5/index.cgi?subroutines_called_with_the_ampersand
Good uses of &foo
There are still good uses of &foo. In general, the & sigil is correct when you're referring to the subroutine, but not calling it. Some examples:
defined &foo
undef &foo
\&foo
It is practically always bad when you're calling the subroutine. However, &foo is slightly more efficient than foo(@_). If you're using this (micro-)optimization, be sure to add a comment that indicates that you know what you're doing, something like:
&foo; # optimization: pass @_ efficiently