2010-11-01 11 views
12

Esto me ha estado volviendo loco durante la última media hora. ¿Hay alguna forma de que agarre una porción de matriz al final de una matriz anónima? He intentado:

(split(' ',$test_line))[1..$#_]

y he intentado: (split(' ',$test_line))[1..-1]

pero aggravatingly, ninguno de los trabajos. Realmente no quiero tener una variable temporal extra instanciada en la matriz intermedia (que no necesito). Y realmente no quiero usar un trazador de líneas feo e ilegible (encontré algunos de esos en línea). ¿Realmente no hay una manera directa de hacer esto?¿Hay alguna manera de agarrar un corte al final de una matriz anónima en Perl?

+2

sospecho que la mejor respuesta será el extra variable de temperatura En su primera línea, el motivo por el que '$ # _' no funciona es que '@ _' no es la lista de la expresión. – aschepler

Respuesta

15

Una lista, que es lo que tiene en su ejemplo, no se puede cortar desde el final. Esto se debe principalmente a que las listas no son estructuras de datos adecuadas en Perl, sino más bien una construcción que el intérprete utiliza para mover datos. Entonces, sabiendo que solo puede cortar una lista desde el principio, sus opciones son colocarla en una variable de matriz y luego cortar, cambiar su algoritmo para devolver lo que desee, o lo siguiente:

Si está asignando esto valor a algo, puede utilizar undef en cada ranura que usted no desea:

my (undef, @list) = split ' ' => $test_line; 

Si publicas algo más de código, puedo revisar.

Alternativamente, puede utilizar algunas herramientas de la programación funcional. El par de funciones drop y take puede ser útil para cambiar el tamaño de una lista sin variables adicionales:

sub take { 
    my $n = shift; 
    @_[0..$n-1] 
} 
sub drop { 
    my $n = shift; 
    @_[$n..$#_] 
} 

y entonces su ejemplo se convierte en

drop 1, split ' ' => $test_line; 

drop 1 se suele llamar también tail

sub tail {drop 1, @_} 

y por supuesto, ya que todos estos son muy cortos, si desea alinearlo:

sub {shift; @_}->(split ' ' => ...) 
+0

Mi idea es la siguiente: my% test_hash = (split ('', $ test_line)) [1 .. $ # _]; En ese caso particular, su enfoque funciona bien, pero esperaba algo un poco más genérico, por lo que cuando no quiero los primeros 10 elementos, no me quedaré atascado escribiendo undef 10 veces ... – Eli

+0

@Eli => por desgracia, esa no es una característica de las divisiones de lista en Perl5. La solución genérica es algo así como 'drop' o' take'. Dependiendo de su contenido, también podría usar grep para eliminar los elementos que no desea. –

+3

Buena respuesta. Hay * soluciones * one-liner, pero realmente no pertenecen al código de producción. – tchrist

2

No creo que se puede especificar un índice para el último elemento de una expresión lista arbitraria, pero ¿qué tal:

split(' ', (split ' ', $test_line, 2)[1]) 

Por cierto, no hay matrices anónimos aquí (o en su pregunta original), solo listas.

+0

No es el más bonito, llamando 'split' dos veces. También podría usar 'split '' '=> ($ test_line = ~ /\s+(.*)/s && $ 1)', pero yo no recomendaría eso. – tchrist

+0

Quizás no sea el más bonito, pero al menos es corto y relativamente sencillo. – Sean

6

Cuando el OP dijo rebanada, pensé en splice:

@allTheWordsExceptTheFirstTwo = splice @{[split' ', $test_line]}, 2; 
@allExceptTheFirstAndLastTwo = splice @{[split' ', $test_line]}, 2, -2; 
0

si usted está abierto a la división en dos ocasiones:

my @g = (split ' ', $test_str)[1..split(' ', $test_str)]; 

o más correctamente (ya dividida devuelve el número de campos encontrados (uno más que el último índice del campo ya que está basado en 0):

my @g = (split ' ', $test_str)[1..split(' ', $test_str)-1]; 

desafortunadamente estos arrojan una advertencia obsoleta bajo pragma de 'advertencias', y anula el contenido de @_ (a menos que esté usando 5.12, entonces está bien, de lo contrario vaya con una variable temporal, sub en línea o un bucle).

3

Puede utilizar rangos negativos en el subíndice de matriz para hacer frente a un número arbitrario de elementos del final:

my $x = join ' ' => 'a' .. 'z'; 
my @x = (split ' ', $x)[-13 .. -1]; 

Sin embargo, esto requiere conocer el número total de elementos en el resultado de split para eliminar solo el primer elemento.

Si esto sucede en un solo lugar, el uso de un bloque do debería funcionar:

my $x = join ' ', 'a' .. 'z'; 
my @x = do { my @y = (split ' ', $x); @y[1 .. $#y] }; 

En su caso, me factorizar toda la operación a una subrutina, si se supone que debe ser utilizado con frecuencia, pasando la cadena más que el resultado de la split a la subrutina (se puede generalizar más allá al permitir que el usuario pase el patrón de fractura, así:

my $x = join ' ', 'a' .. 'g'; 
my @x = skip_first_n_from_split(3, $x); 

print Dump \@x; 

sub skip_first_n_from_split { 
    my ($n, $x) = @_; 
    my @y = split ' ', $x; 
    return @y[$n .. $#y]; 
} 

divertirse:

#!/usr/bin/perl 

use strict; use warnings; 

my $x = join ' ', 1 .. 8; 
my @skippers = map make_skipper(' ', $_), 0 .. 7; 

print "@$_\n" for map $_->($x), @skippers; 

sub make_skipper { 
    my ($pattern, $n) = @_; 

    return sub { 
     my $string = shift; 
     my $i = 0; 
     return [ grep $i++ >= $n, split $pattern, $string ]; 
    } 
} 

Salida:

1 2 3 4 5 6 7 8 
2 3 4 5 6 7 8 
3 4 5 6 7 8 
4 5 6 7 8 
5 6 7 8 
6 7 8 
7 8 
8
2

Esto sólo tiene answered 'limpiamente' en Perlmonks por BrowserUK, esto es lo que haría:

my @slice = sub{ @_[1..$#_] }->(split ' ', $test_line); 
Cuestiones relacionadas