2009-09-22 7 views
6

El código que he escrito es la siguiente:¿Por qué la lista de mi mapa Perl devuelve solo 1?

#!/usr/bin/perl 

my @input = ("a.txt" , "b.txt" , "c.txt") ; 
my @output = map { $_ =~ s/\..*$// } @input ; 

print @output ; 

Mi intención es dejar que el nombre del archivo sin la extensión almacenado en la matriz @output. sino que almacena el valor devuelto por s/// lugar del nombre de archivo modificado en @output, por lo que el resultado es idéntico

1 
1 
1 

así que lo que es la forma correcta de utilizar map en esta situación?

Respuesta

14

Ok, en primer lugar, es probable que desee tener $_ =~ s/\..*$// - tenga en cuenta que falta s en su ejemplo. Además, probablemente se refiera a map no a grep.

En segundo lugar, eso no hace lo que quiere. ¡Eso realmente modifica @input! Dentro de grep (y map, y varios otros lugares), $_ es realmente un alias para cada valor. Entonces, en realidad estás cambiando el valor.

También tenga en cuenta que la coincidencia de patrón no devuelve el valor coincidente; devuelve verdadero (si hay una coincidencia) o falso (si no lo hay). Eso es todo lo que estás viendo.

En su lugar, hacer algo como esto:

my @output = map { 
    (my $foo = $_) =~ s/\..*$//; 
    $foo; 
} @input ; 

Las primeras copias $_ a $foo, y luego modifica $foo. Luego, devuelve el valor modificado (almacenado en $foo). No puede usar return $foo, porque es un bloque, no una subrutina.

+0

@derobert: la 's' que falta es un error de edición, lo he solucionado.¡y he probado tu solución y funciona! –

+0

Me alegra oír que funciona. – derobert

+4

Lista :: MoreUtils http://search.cpan.org/perldoc/List::MoreUtils ofrece 'apply' que es perfecto para este tipo de cosas. Funciona como un 'mapa', pero no altera los valores en el argumento de la matriz. 'use List :: MoreUtils 'apply'; mi @output = apply {s /\..*$//} @input; ' – daotoad

2

Te has perdido el 's' en su reemplazo.

$_ =~ /\..*$// 

debe ser

$_ =~ s/\..*$// 

También usted puede ser mejor usar s/\.[^\.]*$// como su expresión regular para asegurarse de que acaba de quitar la extensión, incluso cuando el nombre de archivo contiene un '' (punto) personaje

+0

@Nikhil: Sí, su expresión regular es mejor, gracias. –

0

En el ejemplo de código falta un s en el operador del partido. Aparte de eso, funcionó bien para mí:

$, = "\n"; 
my @input = ("a.txt" , "b.txt" , "c.txt"); 
my @output = grep { $_ =~ s/\..*$// } @input; 
print @output; 

de salida es:

 
a 
b 
c 
+2

Haz una 'impresión @ entrada' y ten en cuenta cómo tu código altera' @ input', que es inesperado y muy probablemente no deseado. – derobert

+0

Eso es probablemente cierto. – bobbymcr

7

El problema de aliasing $_ los valores de la lista ya fue tratado.

Pero lo que es más: el título de su pregunta dice claramente "mapa", pero su código usa grep, aunque parece que realmente debería usar el mapa.

grep evaluará cada elemento en la lista que proporcione como segundo argumento. Y en el contexto de la lista, devolverá una lista que consta de los elementos de la lista original para los cuales su expresión devolvió verdadero.

map Por otro lado, utiliza la expresión o el argumento de bloque para transformar los elementos del argumento de lista devolviendo una nueva lista que consta de los argumentos transformados del original.

Por lo tanto el problema podría resolverse con un código como éste:

@output = map { m/(.+)\.[^\.]+/ ? $1 : $_ } @input; 

Esto coincidirá con la parte del nombre de archivo que no es una extensión y devolverlo como resultado de la evaluación o devolver el nombre original si no hay extensión

1

derobert muestra una forma correcta de mapear @input a @output.

Quisiera, sin embargo, recomendamos el uso de File::Basename:

#!/usr/bin/perl 

use strict; 
use warnings; 

use File::Basename; 

my @input = qw(a.1.txt b.txt c.txt); 
my @output = map { scalar fileparse($_, qr/\.[^.]*/) } @input ; 

use Data::Dumper; 
print Dumper \@output; 

Salida:

 
C:\Temp> h 
$VAR1 = [ 
      'a.1', 
      'b', 
      'c' 
     ]; 
12

De todas esas respuestas, nadie se limitó a decir que map devuelve el resultado de la última expresión evaluada Lo que sea que haga la última vez es la cosa (o cosas) map devuelve. Simplemente le gusta una subrutina o do que devuelve el resultado de su última expresión evaluada.

Perl v5.14 agrega la sustitución no destructiva, sobre la que escribo en Use the /r substitution flag to work on a copy. En lugar de devolver el número de reemplazos, devuelve la copia modificada. Usar la bandera /r:

my @output = map { s/\..*$//r } @input; 

Tenga en cuenta que no es necesario utilizar el $_ con el operador de unión ya que es el tema por defecto.

+2

Gracias por señalar la bandera '/ r' que desconocía. –

1

Como se mencionó, s /// devuelve el número de sustituciones realizadas, y el mapa devuelve la última expresión evaluada de cada iteración, por lo que su mapa devuelve todos los 1. Una forma de lograr lo que desea es:

s/\..*$// for my @output = @input; 

Otra forma es utilizar filtros de Algorithm::Loops

+0

son los pequeños detalles que te llevan a Perl, una buena razón para que, a menos que creas que entiendas completamente el idioma, debes apegarte a los módulos cuando las cosas se pongan difíciles. – osirisgothra

0

El problema: tanto the s/../.../ operator y Perl's map son imprescindibles, esperando que desee modificar cada elemento de entrada; De hecho, Perl no tiene un builtin para el funcional map que arroje sus resultados sin modificar la entrada.

Al utilizar s, una opción es añadir el r modificador:

#!/usr/bin/perl 

my @input = ("a.txt" , "b.txt" , "c.txt") ; 
my @output = map { s/\..*$//r } @input ; 

print join(' ', @output), "\n"; 

La solución general (sugerido por Derobert) es utilizar List::MoreUtils::apply:

#!/usr/bin/perl 

use List::MoreUtils qw(apply); 

my @input = ("a.txt" , "b.txt" , "c.txt") ; 
my @output = apply { s/\..*$// } @input ; 

print join(' ', @output), "\n"; 

o copiar su definición en su código:

sub apply (&@) { 
    my $action = shift; 
    &$action foreach my @values = @_; 
    wantarray ? @values : $values[-1]; 
} 
Cuestiones relacionadas