2013-11-04 4 views
5

Qual è l'esatta funzione/scopo di * di fronte a _fact e come può essere scritto in modo equivalente?misterioso * davanti al sub nidificato

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); 

risposta

2

check typeglob aliases

esempio di cui sopra dovrebbe essere scritto utilizzando anonima subroutine/chiusura:

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

"Qual è esattamente la funzione/scopo di * davanti _fact" –

+0

@ikegami, che dire di http://codepad.org/80KSqye4? –

+0

Non perde, ma è una soluzione scadente. Rimuove la ricorsione della coda che era ovviamente un fattore guida nel design del sub. '$ n? $ n * fatto ($ n-1): 1' sarebbe stato sufficiente altrimenti. L'eliminazione della ricorsione della coda lo rende inefficiente ovunque. Cambiando il sub esterno per finire con 'my $ rv = $ _fact (...); undef $ _fact; $ rv' sarebbe più appropriato. – ikegami

0

E 'così chiamato typeglob e viene utilizzato per creare alias di tabella. Vedi perldoc reference per maggiori informazioni.

1

Sembra che questo sia un tentativo funky di creare una chiusura assegnando un riferimento al codice typeglob denominato _fact e quindi chiamandolo pseudo-ricorsivo. (nota: un typeglob è il contenitore per tutte le variabili con un nome particolare).

Un modo pressoché equivalente (e molto altro standard) per scrivere questo sarebbe:

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 
} 

... ma, come è stato gentilmente segnalato, che ha una perdita di memoria in esso ... così, io dico solo la chiusura discarica del tutto e scrivere in questo modo:

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

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

(ricordate, l'unica cosa peggiore di ricorsione infinita è ... infinito ricorsione)

+0

@ikegami, in realtà, questo codice non ha una perdita di memoria. Invece, '$ _fact' non è accessibile all'interno del sottotitolo, quindi non può essere utilizzato per le chiamate ricorsive. – cjm

11

Idealmente, l'autore della funzione avrebbe voluto utilizzare

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); 
} 

Purtroppo, che ha una perdita di memoria. Il sotto sub ha un riferimento a $_fact, che contiene un riferimento al sub anonimo. $_fact dovrebbe essere cancellato per interrompere il riferimento all'uscita.

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;  
} 

Ma questo è BRUTTO! Un modo per evitare il problema è l'utilizzo di Y combinator. Un modo molto più semplice per evitare il problema è quello di memorizzare il codice di riferimento in una variabile del pacchetto invece di una variabile lessicale (poiché solo le variabili lessicali sono catturate dai sottotitoli). Questo è ciò che fa il codice che hai postato. Tenete a mente che

*_fact = sub { ... }; 

è fondamentalmente una versione di runtime di

sub _fact { ... } 

Sia assegnare il sub a fessura CODICE del simbolo _fact.

Detto questo, 5.16 ha introdotto una correzione migliore:

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

ha visto il combinatore Y. cliccato +1. – memowe

+0

@ikegami potresti spiegare perché la porzione di valutazione è necessaria? È solo per evitare che un errore generato lasci perdere la memoria? –

+1

@Nate Glenn, Devo interrompere il ciclo di memoria anche in caso di errore (a meno che tu non sappia che il programma uscirà comunque). L'eval non è probabilmente necessario qui perché non prevedo alcun errore di run-time, ma l'ho incluso perché questo è ovviamente un esercizio di apprendimento. Usare la ricorsione per factorial in Perl è follemente dispendioso. – ikegami