2013-11-04 3 views
5

_factの前に*の正確な機能/目的は何ですか?それは等価的にどのように書くことができますか?上記無名サブルーチン/クロージャを使用して記述する必要がありますネストされたサブの前に不思議*

sub fact { 
    my ($n) = @_; 

    local *_fact = sub { 
     my ($n, $prod) = @_; 
     return $prod if $n == 0; 
     return _fact($n-1, $n*$prod); 
    }; 

    return _fact($n, 1); 
} 

fact($n); 

答えて

2

チェックtypeglob aliases

例:それはそうtypeglobと呼ばれていますし、テーブルのエイリアスを作成するために使用され

sub fact { 
    my ($n) = @_; 

    my $_fact; 
    $_fact = sub { 
     my ($n, $prod) = @_; 
     return $prod if $n == 0; 
     return __SUB__->($n-1, $n*$prod); 
    }; 

    return $_fact->($n, 1); 
} 
+0

"_factの前に*の正確な機能/目的は何ですか?" –

+0

@ikegamiどうすればhttp://codepad.org/80KSqye4? –

+0

それは漏れませんが、それは貧弱な解決策です。それは明らかにサブの設計における指導的要因であったテイル再帰を取り除く。 '$ n? $ n * fact($ n-1):1'はそうでなければ十分に成功しています。テール再帰を排除することで、どこでも非効率的になります。外側の部分を 'my $ rv = $ _fact(...);で終わるように変更する。 undef $ _fact; $ rv'がより適切でしょう。 – ikegami

1

_factという名前の型グロブにコード参照を割り当て、それを疑似再帰的に呼び出すことによって、クロージャを作成するのは非常に難しい試みです。 (注:タイプグロブは、特定の名前を持つすべての変数のコンテナです)。

Aほぼ同等(とはるかに標準)これは次のようになり書き方:

sub fact { 
    my ($n) = @_; 

    my $_fact; 

    $fact = sub { .... }; # Assigning code-ref to scalar variable. 

    return $_fact->($n, 1); # Note the arrow syntax to deref the code-ref 
} 

...しかし、親切にそれでメモリリークが発生していること、が指摘されたよう...そう、私は完全に閉鎖をダンプし、そのようにそれを書く言う:

sub fact { 
    my($n,$prod) = @_; 

    return((defined $prod) ? (($n == 0) ? $prod : fact($n-1, $n * $prod)) : fact($n,1)); 
} 

は(無限再帰よりも唯一悪化し、覚えている...無限再帰)

+0

@ikegami、実際にはこのコードにはメモリリークはありません。代わりに、 '$ _fact'はサブの中でアクセスできないので、再帰呼び出しには使用できません。 – cjm

11

理想的には、機能の作者は、メモリリークが発生している残念ながら

sub fact { 
    my ($n) = @_; 

    my $_fact; $_fact = sub { 
     my ($n, $prod) = @_; 
     return $prod if $n == 0; 
     return $_fact->($n-1, $n*$prod); 
    }; 

    return $_fact->($n, 1); 
} 

、使用することが好きだろう。 anon subには$_factへの参照があり、匿名サブの参照が保持されています。終了時に参照を解除するには、$_factをクリアする必要があります。

sub fact { 
    my ($n) = @_; 

    my $_fact; 
    $_fact = sub { 
     my ($n, $prod) = @_; 
     return $prod if $n == 0; 
     return $_fact->($n-1, $n*$prod); 
    }; 

    my $rv; 
    my $e = eval { $rv = $_fact->($n, 1); 1 } ? undef : ([email protected] || 'Unknown'); 
    $_fact = undef; 
    die $e if $e 
    return $rv;  
} 

しかし、それは醜いです!問題を回避する方法の1つはY combinatorです。問題を回避するもっと簡単な方法は、レキシカル変数の代わりにパッケージ変数にコード参照を格納することです(字句変数のみがsubsによってキャプチャされるため)。これはあなたが投稿したコードが行うことです。

*_fact = sub { ... }; 

は、基本的

sub _fact { ... } 

のランタイムバージョンの両方シンボル_factのCODEスロットにサブを割り当てることに留意してください。

、請求、5.16導入良く修正:

use feature qw(current_sub); 

sub fact { 
    my ($n) = @_; 

    my $_fact = sub { 
     my ($n, $prod) = @_; 
     return $prod if $n == 0; 
     return __SUB__->($n-1, $n*$prod); 
    }; 

    return $_fact->($n, 1); 
} 
+1

ソーYコンビネータ。 +1をクリックしました。 – memowe

+0

@ikegamiなぜ評価部分が必要なのか説明できますか?スローされたエラーがメモリのリークを許可しないようにするだけですか? –

+1

@Nate Glenn、エラー時でもメモリサイクルを壊す(プログラムが途中で終了することが分かっている場合を除きます)。実行時エラーを予期しないので、 'eval'はここでは必要ありませんが、これは明らかに学習の練習です。 Perlで階乗の再帰を使うことは、無駄に無駄です。 – ikegami

関連する問題