2016-03-26 4 views
5

注意:この質問に記載されている閉鎖は単なる便利な例です。私が実際に作業しているものは、これよりもかなり複雑です。 IOW、この閉鎖の詳細は無視してください。 AFAICTは、親スコープ内のレキシカル変数を参照するということです。 List::Util::reduceへの呼び出しの最初の引数は、ネストされたクロージャを参照して置き換えられるようにList :: Util :: reduceの最初の引数としてネストされたクロージャを使用する方法?


は、私は以下のサブfooを再定義します。

use strict; 
use warnings FATAL => 'all'; 
use List::Util; 

sub foo { 
    my ($x, $y) = @_; 
    return List::Util::reduce { $y->[ $b ]{ $x } ? $a + (1 << $b) : $a } 0, 0 .. $#$y; 
} 

私の最初の試みは、このでした:

sub foo { 
    my ($x, $y) = @_; 
    sub z { 
    our ($a, $b); 
    return exists $y->[ $b ]{ $x } ? $a | (1 << $b) : $a; 
    } 
    return List::Util::reduce \&z, 0, 0 .. $#{ $y }; 
} 

...しかし、これはそのVariable "$y" will not stay sharedを言って、警告になります。

これまで私はこの種のエラーが発生していたことを知りましたが、入れ子になったsubの定義を変数に割り当てられた匿名のサブセットに置き換えることが唯一の方法です。したがって、私はこれを代わりに試しました:

sub foo { 
    my ($x, $y) = @_; 
    my $z = sub { 
    our ($a, $b); 
    return exists $y->[ $b ]{ $x } ? $a | (1 << $b) : $a; 
    }; 
    return List::Util::reduce($z, 0, 0 .. $#{ $y }); 
} 

ここでエラーはType of arg 1 to List::Util::reduce must be block or sub {} (not private variable)と表示されます。

これは本当にわかりません。 reduceの最初の引数としてサブリファレンスを渡すのはなぜサポートされないのですか?


PSは:

sub foo { 
    my ($x, $y) = @_; 
    my $z = sub { 
    our ($a, $b); 
    return exists $y->[ $b ]{ $x } ? $a | (1 << $b) : $a; 
    }; 
    return List::Util::reduce { $z->($a, $b) } 0, 0 .. $#{ $y }; 
} 

...しかし、私が本当に知りたいのですが、(A)(List::Util::reduceの最初の引数としてSUBREFを使用してと間違って何等がある:以下は動作しませんこの変形をサポートするための技術的な障害? (b)List::Util::reduceにネストされたクロージャを渡すより直接的なアプローチ({ $z->($a, $b) })がありますか?

答えて

3

reduceは、&@のプロトタイプを有する。

reduce BLOCK LIST 
reduce sub BLOCK, LIST # The first form is short for this. 
reduce \&NAME, LIST 
reduce \&$NAME, LIST # Same as third form, but via reference (short form) 
reduce \&BLOCK, LIST # Same as third form, but via reference (long form) 

あなたは、次の同等のステートメントのいずれかを使用できます:

return reduce { exists $y->[ $b ]{ $x } ? $a | (1 << $b) : $a } 0, 0 .. $#$y; 

# Long way of writing the first. 
return reduce(sub { exists $y->[ $b ]{ $x } ? $a | (1 << $b) : $a }, 0, 0 .. $#$y); 

my $z = sub { exists $y->[ $b ]{ $x } ? $a | (1 << $b) : $a }; 
return reduce(\&$z, 0, 0 .. $#$y); 
それは呼び出しは、次の構文のいずれかを使用しなければならないことを意味します3210

プロトタイプを上書きするオプションもあります。

my $z = sub { exists $y->[ $b ]{ $x } ? $a | (1 << $b) : $a }; 
return &reduce($z, 0, 0 .. $#$y); 
+0

最後のフォーム 'reduce \&BLOCK、LIST'の使い方は?私は以下を試しました: 'perl -mList :: Util -E 'List :: Util :: reduce \&{$ a> $ b? $ a:$ b}、1..10''となり、セグメント化エラーが発生しました。 –

+1

ブロックは現場で評価され、 '\&{$ z}'のように参照をサブに返さなければなりません – ikegami

4

List::Util::reduceは、コールに渡されたパラメータにコンパイル時チェックを指定したPerl プロトタイプを使用しています。サブルーチン参照を含むスカラー変数を渡すことはコンパイル時に検証できないため、Perlはそれを許可しません

これはソリューションへのルートです。通常、アンパサンド文字&を使用してサブルーチンを呼び出すために悪い習慣と考えられているが、この場合には、これはのように、一般的には不可能であることを

sub foo { 

    my ($x, $y) = @_; 

    my $z = sub { 
    our ($a, $b); 
    return exists $y->[ $b ]{ $x } ? $a | (1 << $b) : $a; 
    }; 

    return &List::Util::reduce($z, 0, 0 .. $#{ $y }); # Defeat prototype 
} 

注意を必要とされるものであるプロトタイプチェックを、打ち負かしますプロトタイプは、配列やハッシュを参照渡しにするなど、スカラーコンテキストを強制的に実行し、&を使用してそのようなサブルーチンを呼び出すと、これらの動作も無効になります。しかし、reduceは、&@というプロトタイプを持っています。これはブロックを意味します。sub { }(ここではsubの部分は省略可能です)、プロトタイプのないサブルーチンの通常の動作であるスカラーのリストがありますので、唯一の効果は$zサブルーチン参照のために渡される

+2

「reduce」のプロトタイプはどのように決定しましたか? [source code](https://metacpan.org/source/PEVANS/Scalar-List-Utils-1.45/lib/List/Util.pm)から[metacpan.org](https:///metacpan.org/)でもプロトタイプはありません(単に 'XSLoader :: load()'と呼ばれます)。 –

+3

@HåkonHægland:モジュールはC言語で記述されているので、[XSファイル 'ListUtil.xs'](http://cpansearch.perl.org/src/PEVANS/Scalar-List-Utils-1.45/ListUtil.xs)にあります。 line 369 – Borodin

関連する問題