2010-01-18 7 views
6

Últimamente he estado pensando mucho sobre programación funcional. Perl ofrece bastantes herramientas para ir por ese camino, sin embargo, hay algo que aún no he podido encontrar.¿Cómo haría el equivalente al Enumerator.detect de Prototype en Perl con la menor cantidad de código?

prototipo tiene la función de detectar para enumeradores, las descripciones es simplemente esto:

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

Enumerador en este caso es cualquier lista mientras iterador es una referencia a una función, que se aplica a su vez en cada elemento de la lista.

Estoy buscando algo como esto para aplicar en situaciones donde el rendimiento es importante, es decir, cuando se detiene al encontrar un partido ahorra tiempo sin tener en cuenta el resto de la lista.

También estoy buscando una solución que no implique cargar ningún módulo adicional, por lo tanto, si es posible, debe hacerse solo con edificios incorporados. Y si es posible, debería ser lo más concisos esto, por ejemplo:

my @result = map function @array; 
+7

Es bueno ver una pregunta de la forma "¿Cuál es el idioma X equivalente a A desde el lenguaje Y?" con una explicación de lo que A hace. Gracias. – daotoad

Respuesta

15

Usted dice que no quiere un módulo, pero esto es exactamente lo que la función first en List::Util hace. Es un módulo central, por lo que debería estar disponible en todas partes.

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

Si insiste en no usar un módulo, puede copiar la implementación fuera de List :: Util. Si alguien supiera una forma más rápida de hacerlo, estaría allí. (Tenga en cuenta que List :: Util incluye una implementación XS, por lo que es probablemente más rápido que cualquier enfoque puro de Perl. También tiene una versión Perl pura de first, en List :: Util :: PP.)

Tenga en cuenta que el valor que se está probando se pasa a la subrutina en $_ y no como como parámetro. Esta es una conveniencia cuando usa el formulario first { some condition} @values, pero es algo que debe recordar si está utilizando una subrutina normal. Algunos ejemplos más:

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 List :: Util y List :: MoreUtils son muy funcionales, después de todo, incluso contienen un 'reduce'. :) – Ether

+0

Gracias por explicarme.Mi principal queja con respecto a entrar en otro módulo fue que mi repetición era un tanto exagerada, pero en este momento creo que no puedo evitar recurrir a Toolset. También, en re su última explicación, en realidad los usaré en estos dos formularios: my $ res = first some_func, @array; Donde somefunc es un sub que actúa sobre $ _; y: my $ res = first some_func ($ _, 5), @array; Donde some_func es un sub que actúa en @_. (Al menos espero que funcionen de la misma manera que lo hacen para el mapa.) (Argh, sin formato en los comentarios. :() – Mithaldu

5

No comprobado ya que no tengo Perl en esta máquina, pero:

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

luego usarlo como:

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

Tenga en cuenta que esto no lo hace distinga entre ninguna coincidencia en la lista y uno de los elementos en la lista es un valor indefinido y tiene esa coincidencia.

+3

Eso es más o menos lo mismo que la versión en List :: Util :: PP (excepto que uno no no use una variable léxica en el bucle for). – cjm

+0

Es bueno saberlo. Como dije, esto fue totalmente infundado. – Dan

+2

El motivo por el que no se utiliza una variable léxica es que permite al sub referirse al valor que se prueba como '$ _' en lugar de' $ _ [0] '(como tendrías que hacer en tu versión). – cjm

4

Justo ya que no es aquí, una definición de función de Perl primero que localiza $_ de su bloque:

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

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

A pesar de que no tendrán ningún problema, no abogan por el uso de esta versión, ya que es un núcleo List::Util módulo (instalado por defecto), y su implementación de first generalmente usará la versión XS (escrita en C) que es mucho más rápida.

Cuestiones relacionadas