2011-10-25 8 views
10

escribí este código en Perl:Conversión de resultado de contexto de lista a matriz en una línea en perl?

shift(@interfaces = qx'ifconfig -s'); 

Y tiene este error:

Type of arg 1 to shift must be array (not list assignment) 

Cuando escribo de esta manera:

@interfaces = qx'ifconfig -s'; 
shift @interfaces; 

Se hace lo que yo quiero, que es para obtener el resultado del comando ifconfig como una matriz de líneas y eliminar el primer elemento de la matriz (que es un encabezado, no una interfaz real).

Mi preferencia personal es escribir esto como un trazador de líneas. Me parece que los paréntesis en el primer ejemplo deben hacer que la tarea se resuelva por completo, permitiendo así que shift vea las interfaces como una matriz, pero claramente Perl piensa que es una "asignación de lista".

Esta es sin duda una pregunta fácil para los gurús perl pero he buscado en Google y en Google y no he encontrado la iluminación.

Si alguien por favor proporciona la semántica específica para lograr lo que quiero en una línea, lo agradecería. Si también te gustaría tomar el tiempo para explicar por qué mi primera versión no funciona, estaría eternamente agradecido (enseña a un hombre a pescar y todo eso).

Gracias de antemano por su ayuda.

Respuesta

13

Como se vio, shift requiere un literal de matriz, no el resultado de una asignación. Esto se debe a que cuando perl analiza shift @interfaces, en realidad lo está reescribiendo en algo como &CORE::shift(\@interfaces) y no puede tomar una referencia de una tarea y obtener una referencia de matriz.

Se podría romperlo en dos líneas que usted ha encontrado, que podría ocultar la asignación dentro de una falta de referencia entre corchetes como mafia espectáculos, o simplemente podría tirar el primer valor:

(undef, @interfaces) = qx'ifconfig -s'; 

undef en una posición lvalue es un marcador de posición para valores que no necesita.

(análisis de shift ha cambiado un poco en Perl 5.14+, pero el argumento anterior aún mantiene)


unas cuantas más formas que probablemente no debería utilizar, ordenó sólo aumentando la longitud :)

my @interfaces = sub {shift; @_}->(qx'ifconfig -s'); 

my @interfaces = sub {@_[1..$#_]}->(qx'ifconfig -s'); 

my @interfaces = map {@$_[1..$#$_]} [qx'ifconfig -s']; 

my @interfaces = map {shift @$_; @$_} [qx'ifconfig -s']; 

our @interfaces; shift @{*interfaces = [qx'ifconfig -s']}; 

my @interfaces = sub {*_ = [qx'ifconfig -s']; shift; @_}->(); 
+0

esta es una gran solución pero desde una perspectiva de mantenimiento quiero usar shift para enfatizar a un desarrollador futuro que estoy tirando el primer elemento de una matriz. Definitivamente estoy dando una respuesta a su respuesta, pero no es exactamente lo que quiero porque creo que no usar el cambio disminuye la claridad del código (YMMV, por supuesto). Gran respuesta sin embargo. – par

+0

Al final he decidido que esto será lo suficientemente claro una vez que se entienda el modismo de asignación de undef (ser yo y los que finalmente tienen que mantener lo que estoy trabajando). Siempre debe haber un equilibrio entre la claridad del código y la facilidad de uso más adelante. La respuesta de Mob es asombrosa, pero creo que terminar con una matriz y no una matriz de referencia finalmente hará que el código sea más fácil de seguir. – par

+1

Ok, obtienes un voto positivo por esa locura hacia el final, ** y ** para clasificarlos por duración. =) – TLP

7

shift @{EXPR} es una sintaxis válida, por lo

shift @{$interfaces = [qx'ifconfig -s']} 

le dará una referencia a un array que tiene el primer elemento eliminado.

descubrí esto desde la salida diagnostics en llamar shift de asignación lista:

$ perl -Mdiagnostics -e 'print shift(@a = (2,3,4))' 
 
Type of arg 1 to shift must be array (not list assignment) at -e line 1, at end of line 
Execution of -e aborted due to compilation errors (#1) 
    (F) This function requires the argument in that position to be of a 
    certain type. Arrays must be @NAME or @{EXPR}. Hashes must be 
    %NAME or %{EXPR}. No implicit dereferencing is allowed--use the 
    {EXPR} forms as an explicit dereference. See perlref. 

Perl hace cumplir este comportamiento en cualquier subrutina definida por el usuario o BuiltIn que se prototipo con \@ o \% caracteres. El prototipo es una pista para el intérprete de que Perl debe tratar una matriz o un argumento de la función hash como una matriz o tipo hash, y no tratar de desenrollar la lista en varios argumentos.

Una forma de pensarlo (aunque no estoy seguro de si esto es exacto para los builtins) es que Perl leerá la matriz o variable hash de su lista de argumentos a una llamada de función, pero en realidad pasará una referencia a esa variable a la función de prototipo. Entonces el intérprete necesita identificar una matriz o hash en su lista de argumentos, y necesita poder obtener una referencia a esa matriz o hash. Perl no hace o no puede (agitando las manos aquí) hacer eso con una expresión lista de asignación - en cuenta que el resultado de

\(@a = (1,2,3)) 

es una lista de 3 referencias a escalares, no una referencia a una lista con 3 escalares.

Se puede ver los prototipos (si lo hay) para la mayoría de las órdenes internas Perl con la función prototype:

$ perl -e 'print prototype("CORE::shift")'  ===> \@ 
$ perl -e 'print prototype("CORE::each")'  ===> \% 
$ perl -e 'print prototype("CORE::push")'  ===> \@@ 
+0

Probé el primer ejemplo que me diste utilizando la notación {EXPR} y la matriz de interfaces está vacía después del comando, así que aparentemente eso no funcionará. – par

+1

@par debería funcionar. Sin embargo, la matriz ahora estará en '@ $ interfaces', no en' @ interfaces'. – mercator

+0

'@ $ interfaces' de hecho funciona. Entiendo '@ {EXPR}' como una construcción de matriz, ¿para qué son los corchetes alrededor del comando qx? – par

3

lo más cerca que podía llegar a conseguir esto en un chiste:

perl -e '@interfaces = (qx|ifconfig -s|)[1 .. 1000]; print @interfaces' 

Esto está tomando un corte del índice 1 al índice 1000 y supone que su salida ifconfig no es más de 1000 líneas. Obviamente, esta es una horrible práctica de programación, pero funciona en la mayoría de los casos y hace lo que hace la pregunta.

+0

Un esfuerzo valiente para estar seguro pero tienes razón, desde el punto de vista de las prácticas de programación, alguien seguramente me arrojaría huevos. – par

3

Perl podría no ser la manera más fácil (o la más legible manera, debo decir) de hacerlo, si quiere restringirse a usar solo una línea. He aquí una solución que solucione el comando shell en su lugar:

@interfaces = qx(ifconfig -s | tail -n +2); 
+0

Otra buena respuesta pero no tan perl-y como me gustaría;) – par

1

Prueba esto:

shift qw(this that the other); 

obtendrá el mismo mensaje de error. El comando shiftdebe tomar una lista de variables y no una lista. Después de todo, hay dos efectos principales con el comando shift.

  1. devuelve el valor del primer elemento de la lista
  2. También elimina el valor del primer elemento de la variable de lista. Si no hay una variable de lista, shift ing no tendría ningún sentido.

En el ejemplo, el (@interfaces = qx 'ifconfig -s') está fijando @interfaces y devuelve el valor de la lista @interfaces y no la propia variable al comando shift.

Mob's respuesta ayuda un poco.Vas a obtener una referencia lista, pero luego, ya sea que usted tiene que mantener la eliminación de referencias, o establecer una variable de lista real:

shift @{$interfaces = [qx'ifconfig -s']} 
foreach my $entry (@{$interfaces}) { #Have to dereference 
    say "Interface: $entry"; 
} 
@interfaces = @{$interfaces};   #This will also work. 

Si el propósito era salvar algo de tecleo, estableciendo una variable de lista real de la variable de referencia dereferece no guarda nada. Y, al usar una referencia en lugar de una lista real en el resto de su programa, simplemente agregará una capa de complejidad y deificación.

Si realmente sólo la creación @interfaces no incluir el primer elemento de la lista devuelta, se puede hacer algo como esto:

(my $garbage, @interfaces) = qw(ifconfig -s); 

El primer valor de la lista será devuelto en $garbage, un tiro variable de distancia. El resto de la lista se sorberá por @interfaces. Esto es limpio y bastante fácil de entender lo que está sucediendo.

Eric Strom ahora veo lo ha hecho aún mejor:

(undef, @interfaces) = qw(ifconfig -s); 

Usted ni siquiera tiene una variable que tirar.

Ahora me voy a quedar despierto toda la noche preocupándome de los cambios que ha hecho Perl 5.14 en el análisis del comando shift.

2

Comenzando con la versión 5.10 de Perl, puede usar la declaración de variable state para administrar la persistencia de una variable sin tener que predefinir con my fuera del ciclo.

use 5.10.0; 
my @interfaces = grep {state $i++} qx'ifconfig -s'; 

sí, puede hacerlo sin estado, pero es un caso de uso perfecto para él. Aquí está el código análogo sin state y la misma lexicidad w.r.t $i.

my @interfaces; 
{ 
    my $i; 
    @interfaces = grep {$i++} qx'ifconfig -s'; 
} 

o

my @interfaces = do { my $i; grep {$i++} qx'ifconfig -s' }; 

, por supuesto, no puede preocuparse por $i 's lexicalidad y simplemente hacer

my $i; 
my @interfaces = grep {$i++} qx'ifconfig -s'; 

o puede engañar

my @interfaces = grep {$|++} qx'ifconfig -s' 

pero que se rompe si confías en $| en otro lugar. No importa todo eso, solo use state.

Cuestiones relacionadas