2010-01-18 8 views
6

Ultimamente ho pensato molto alla programmazione funzionale. Perl offre alcuni strumenti per andare in quel modo, tuttavia c'è qualcosa che non sono stato ancora in grado di trovare.Come farei l'equivalente di Enumerator.detect di Prototype in Perl con il minor numero di codice?

prototipo ha la funzione di rilevamento per enumeratori, le descrizioni è semplicemente questo:

Enumerator.detect(iterator[, context]) -> firstElement | undefined 
Finds the first element for which the iterator returns true. 

enumeratore in questo caso è alcuna lista mentre iteratore è un riferimento ad una funzione, che viene applicato a sua volta su ogni elemento la lista.

Sto cercando qualcosa di simile da applicare in situazioni in cui la prestazione è importante, vale a dire quando fermarsi incontrando una partita fa risparmiare tempo ignorando il resto della lista.

Sto anche cercando una soluzione che non implichi il caricamento di alcun modulo aggiuntivo, quindi se possibile dovrebbe essere eseguita solo con builtin. E, se possibile, dovrebbe essere il più conciso questo ad esempio:

my @result = map function @array; 
+7

È bello vedere una domanda sul modulo "Qual è il langauge X equivalente ad A di langauge Y?" con una spiegazione di cosa fa A. Grazie. – daotoad

risposta

15

Tu dici che non vuoi un modulo, ma questo è esattamente ciò che la funzione first in List::Util fa. Questo è un modulo principale, quindi dovrebbe essere disponibile ovunque.

use List::Util qw(first); 
my $first = first { some condition } @array; 

Se si insiste a non utilizzare un modulo, è possibile copiare l'implementazione da List :: Util. Se qualcuno sapesse un modo più veloce per farlo, sarebbe lì dentro. (Si noti che List :: Util include un'implementazione XS, quindi è probabilmente più veloce di qualsiasi approccio Perl puro. Inoltre ha una versione Perl pura di first, in List :: Util :: PP.)

Nota che il valore da testare viene passato alla subroutine in $_ e non come parametro. Questa è una comodità quando si utilizza il modulo first { some condition} @values, ma è qualcosa che è necessario ricordare se si sta utilizzando una subroutine regolare. Alcuni altri esempi:

use 5.010; # I want to use 'say'; nothing else here is 5.10 specific 
use List::Util qw(first); 

say first { $_ > 3 } 1 .. 10; # prints 4 

sub wanted { $_ > 4 }; # note we're using $_ not $_[0] 
say first \&wanted, 1 .. 10; # prints 5 

my $want = \&wanted;   # Get a subroutine reference 
say first \&$want, 1 .. 10; # This is how you pass a reference in a scalar 

# someFunc expects a parameter instead of looking at $_ 
say first { someFunc($_) } 1 .. 10; 
+4

+1 Elenco :: Util e Elenco :: MoreUtils sono molto funzionali - dopo tutto, contengono addirittura un 'reduce'. :) – Ether

+0

Grazie per aver spiegato.La mia lamentela principale per aver ottenuto un altro modulo era che il mio boilerplate stava diventando un po 'troppo, ma a questo punto credo di non poter evitare di creare Toolset. Inoltre, nella tua ultima spiegazione, in realtà le userò in queste due forme: my $ res = first some_func, @array; Dove somefunc è un sub-acting su $ _; e: my $ res = first some_func ($ _, 5), @array; Dove some_func è un sub-acting su @_. (Almeno spero che funzionino allo stesso modo per la mappa.) (Argh, nessuna formattazione nei commenti. :() – Mithaldu

5

testato dal momento che non ho Perl su questa macchina, ma:

sub first(\&@) { 
    my $pred = shift; 
    die "First argument to "first" must be a sub" unless ref $pred eq 'CODE'; 
    for my $val (@_) { 
     return $val if $pred->($val); 
    } 
    return undef; 
} 

quindi utilizzarlo come:

my $first = first { sub performing test } @list; 

Si noti che questo non lo fa distinguere tra nessuna corrispondenza nella lista e uno degli elementi nella lista è un valore indefinito e avere quella corrispondenza.

+3

È praticamente la stessa della versione in List :: Util :: PP (eccetto che non si esegue usare una variabile lessicale nel ciclo for) – cjm

+0

Buono a sapersi, come ho detto questo era totalmente a pagamento – Dan

+2

Il motivo per cui non si utilizza una variabile lessicale è che consente al sub di riferirsi al valore testato come '$ _' invece di' $ _ [0] '(come dovresti fare nella tua versione) – cjm

4

Solo perché il suo non è qui, una definizione di funzione Perl di prima che localizza $_ per il suo blocco:

sub first (&@) { 
    my $code = shift; 
    for (@_) {return $_ if $code->()} 
    undef 
} 

my @array = 1 .. 10; 
say first {$_ > 5} @array; # prints 6 

Anche se funzionerà bene, io non sostengo utilizzando questa versione, dal momento che è un nucleo List::Util modulo (installato di default), e la sua implementazione di first userà solitamente la versione XS (scritta in C) che è molto più veloce.

Problemi correlati