web-dev-qa-db-ja.com

Perl 5の関数プロトタイプが悪いのはなぜですか?

別のStack Overflowの質問Leon Timmermans アサート:

プロトタイプを使用しないことをお勧めします。それらには用途がありますが、ほとんどの場合ではなく、間違いなくこの用途ではありません。

なぜこれが真実なのか(またはそうでないのか)?私はほとんど常にPerl関数のプロトタイプを提供していますが、それを使用することについて他の誰かが悪いことを言うのを見たことはありません。

116
Alnitak

プロトタイプを正しく使用すれば、悪くありません。難点は、Perlのプロトタイプが、人々がしばしば期待するように機能しないことです。他のプログラミング言語のバックグラウンドを持つ人々は、プロトタイプが関数呼び出しが正しいことをチェックするメカニズムを提供することを期待する傾向があります。つまり、正しい数とタイプの引数を持っているということです。 Perlのプロトタイプは、このタスクには適していません。悪いのはmisuseです。 Perlのプロトタイプには、特異で非常に異なる目的があります。

プロトタイプを使用すると、組み込み関数のように動作する関数を定義できます。

  • 括弧はオプションです。
  • コンテキストは引数に課されます。

たとえば、次のような関数を定義できます。

sub mypush(\@@) { ... }

そしてそれを

mypush @array, 1, 2, 3;

\を記述しなくても、配列への参照を取得できます。

簡単に言うと、プロトタイプを使用すると、独自の構文糖を作成できます。たとえば、Mooseフレームワークはこれらを使用して、より一般的なOO構文をエミュレートします。

これは非常に便利ですが、プロトタイプは非常に制限されています。

  • それらはコンパイル時に表示されなければなりません。
  • それらはバイパスできます。
  • コンテキストを引数に伝播すると、予期しない動作が発生する可能性があります。
  • 厳密に規定された形式以外を使用して関数を呼び出すことが困難になる場合があります。

すべての詳細については、perlsubの Prototypes を参照してください。

121
Michael Carman

問題は、Perlの関数プロトタイプは、人々が思うように動作しないことです。その目的は、Perlの組み込み関数のように解析される関数を作成できるようにすることです。

まず、メソッド呼び出しはプロトタイプを完全に無視します。 OOプログラミングを行っている場合、メソッドがどのプロトタイプを持っているかは関係ありません。

第二に、プロトタイプは厳密に強制されていません。 &function(...)でサブルーチンを呼び出す場合、プロトタイプは無視されます。したがって、実際には型の安全性は提供されません。

第三に、彼らは不気味な距離での行動です。 (特に_$_プロトタイプ。デフォルトのリストコンテキストではなく、対応するパラメーターがスカラーコンテキストで評価されます。)

特に、配列からパラメーターを渡すのが難しくなります。例えば:

_my @array = qw(a b c);

foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);

sub foo ($;$$) { print "@_\n" }

foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);
_

プリント:

_a b c
a b
a b c
3
b
a b c
_

main::foo() called too early to check prototypeに関する3つの警告とともに(警告が有効な場合)。問題は、スカラーコンテキストで評価された配列(または配列スライス)が配列の長さを返すことです。

組み込みのように機能する関数を作成する必要がある場合は、プロトタイプを使用します。それ以外の場合は、プロトタイプを使用しないでください。

注:Perl 6では、完全に改良された非常に便利なプロトタイプが使用されます。この回答は、Perl 5にのみ適用されます。

69
cjm

上記の2つのポスターに同意します。一般的に、$は避けてください。プロトタイプは、ブロック引数(&)、グロブ(*)、または参照プロトタイプ(\@\$\%\*

30
Leon Timmermans

一部の人々は、Perlサブルーチンプロトタイプを見て、それが意味しないことを意味すると考えています。

sub some_sub ($$) { ... }

Perlにとって、これはパーサーが2つの引数を予期することを意味します。これは、組み込みコードのように動作するサブルーチンを作成できるPerlの方法です。これらはすべて、後続のコードに何を期待するかを知っています。 perlsub でプロトタイプについて読むことができます

ドキュメントを読むことなく、人々はプロトタイプが実行時の引数チェックまたは他の言語で見た類似のものを参照していると推測します。人々がPerlについて推測するほとんどのものと同様に、彼らは間違っていることが判明しました。

ただし、Perl v5.20からは、Perlには、これを書いている段階で実験的な機能があり、ユーザーが期待するものや、より多くのものを提供します。 Perlの サブルーチンシグネチャ は、実行時の引数のカウント、変数の割り当て、およびデフォルト設定を行います。

use v5.20;
use feature qw(signatures);
no warnings qw(experimental::signatures);

animals( 'Buster', 'Nikki', 'Godzilla' );

sub animals ($cat, $dog, $lizard = 'Default reptile') { 
    say "The cat is $cat";
    say "The dog is $dog";
    say "The lizard is $lizard";
    }

これは、プロトタイプを検討している場合におそらく必要な機能です。

4
brian d foy