Pregunta ¿Por qué funciona el operador de cabras?


La diferencia entre las matrices y las listas y entre la lista y el contexto escalar se discutió bastante en la comunidad de Perl el año pasado (y todos los años, realmente). He leído artículos de cromático y friedo, tanto como esta nodo recomendado monjes. Estoy tratando de entender al operador de cabras, documentado en perlsecret.

Aquí hay un código que utilicé para estudiarlo:

# right side gets scalar context, so commas return rightmost item
$string = qw(stuff junk things); 
say $string; # things

# right side gets list context, so middle is list assigned in scalar context
$string = () = qw(stuff junk things);
say $string; # 3

# right side gets list context, so creates a list, assigns an item to $string2, and
# evaluates list in scalar context to assign to $string
$string = ($string2) = qw(stuff junk things);
say $string; # 3
say $string2; # stuff

Creo que he llegado lo suficientemente lejos como para entender toda la lista y el funcionamiento del contexto escalar. El operador de coma en contexto escalar devuelve su lado derecho, por lo que el primer ejemplo simplemente asigna el último elemento de la expresión de coma (sin comas) a $string. En los otros ejemplos, la asignación de la expresión de coma a una lista lo pone en contexto de lista, por lo que se crea una lista, y las listas evaluadas en contexto escalar devuelven su tamaño.

Hay 2 partes que no entiendo

Primero, se supone que las listas son inmutables. Esto es enfatizado repetidamente por friedo. Supongo que esa asignación a través de = de la lista a la lista distribuye las asignaciones de los elementos en una lista a los elementos en la otra lista, por lo que en el segundo ejemplo $string2 se pone 'stuff'y por qué podemos desempaquetar @_ a través de la asignación de lista. Sin embargo, no entiendo cómo la asignación a (), una lista vacía, posiblemente podría funcionar. Con mi comprensión actual, dado que las listas son inmutables, el tamaño de la lista permanecería 0, y luego se asignó el tamaño a $stuff en los ejemplos 2 y 3 le daría el valor 0. ¿Las listas no son realmente inmutables?

En segundo lugar, he leído muchas veces que las listas no existen realmente en contexto escalar. Pero la explicación del operador de cabras es que se trata de una asignación de lista en contexto escalar. ¿No es esto un contraejemplo a la afirmación de que las listas no existen en el contexto escalar? ¿O algo más está pasando aquí?

Actualizar: Después de entender la respuesta, creo que un par adicional de paréntesis ayuda a conceptualizar cómo funciona:

$string = ( () = qw(stuff junk things) );

Dentro de los parens, el = es una asignación a un 'agregado', y también lo es un operador de asignación de lista (que es diferente del operador de asignación escalar, y que no debe confundirse con "contexto de lista"; la asignación de lista y escalar puede ocurrir en contexto escalar o de lista) ) () no cambia de ninguna manera = tiene un valor de retorno en Perl, y el resultado de la asignación de la lista se asigna a $string a la izquierda =. Asignación a $string da contexto escalar al RHS (todo en los parens), y en el contexto escalar, el valor devuelto del operador de asignación de lista es la cantidad de elementos en el RHS.

En su lugar, puede poner la asignación de la lista RHS en contexto de lista:

($string) = ( () = qw(stuff junk things) );

De acuerdo a perlop la asignación de lista en el contexto de la lista devuelve la lista de valores l asignados, que aquí está vacía ya que no hay nada a asignar en (). Entonces aquí $ cadena sería undef.


9
2018-01-10 06:44


origen


Respuestas:


Es útil recordar que en Perl, la asignación es una expresión, y que debería estar pensando en el valor de la expresión (el valor del operador de asignación), no "el valor de una lista".

El valor de la expresión qw(a b) es ('a', 'b') en el contexto de la lista y 'b' en contexto escalar, pero el valor de la expresión (() = qw(a b)) es () en el contexto de la lista y 2 en contexto escalar. Los valores de (@a = qw(a b)) sigue el mismo patrón Esto es porque pp_aassign, el operador de asignación de lista elige devolver un recuento en contexto escalar:

else if (gimme == G_SCALAR) {
    dTARGET;
    SP = firstrelem;
    SETi(lastrelem - firstrelem + 1);
}

(pp_hot.c línea 1257; los números de línea están sujetos a cambios, pero es casi el final de PP(pp_aassign).)

Entonces, aparte de la valor del operador de asignación es el efecto secundario del operador de asignación. El efecto secundario de la asignación de lista es copiar valores desde su lado derecho a su lado izquierdo. Si el lado derecho se queda sin valores primero, los elementos restantes del lado izquierdo se ponen undef; si el lado izquierdo se queda sin valores primero, los elementos restantes del lado derecho no se copian. Cuando se le da un LHS de (), la asignación de lista no copia nada en absoluto. Pero el valor de la tarea en sí sigue siendo el número de elementos en el RHS, como se muestra en el fragmento de código.


6
2018-01-12 00:37



Tu no entiendes. Las listas evaluadas en contexto escalar no obtienen su tamaño. De hecho, es casi imposible tener una lista en contexto escalar. Aquí tiene una asignación escalar con dos operandos, una variable escalar a la izquierda y una asignación de lista a la derecha (dado el contexto escalar por la asignación escalar). Listar asignaciones en contexto escalar evaluar la cantidad de elementos en el derecho de la asignación.

Entonces, en:

1 $foo
2 =
3 ()
4 =
5 ('bar')

2, una asignación escalar, da 1 y 4 contexto escalar. 4, una asignación de lista, da el contexto de la lista 3 y 5, pero, sin embargo, se encuentra en contexto escalar y regresa de manera apropiada.

(Cuando = es una asignación de lista o una asignación escalar determinada exclusivamente por la sintaxis circundante, si el operando de la izquierda es un hash, matriz, rebanada de hash, porción de matriz o entre paréntesis, es una asignación de lista, de lo contrario es un escalar asignación.)

Este tratamiento de asignaciones de listas en contexto escalar hace posible un código como:

while ( my ($key, $value) = each %hash ) {

donde list-context cada uno es un iterador que devuelve (en el contexto de la lista) una clave y valor para cada llamada, y una lista vacía cuando finaliza, dando al tiempo un 0 y terminando el ciclo.


10
2018-01-10 06:54



Primero, "lista" es un término ambiguo. Incluso la pregunta lo usa para referirse a dos cosas diferentes. Sospecho que podrías estar haciendo esto sin darte cuenta, y que esta es una parte importante de la causa de tu confusión.

Usaré "un valor de lista" para indicar lo que devuelve un operador en el contexto de la lista. Por el contrario, "el operador de la lista" se refiere al operador EXPR,EXPR,EXPR,... también conocido como "el operador de coma"[1].

Segundo, deberías leer Mini-Tutorial: Escalar contra Operador de Asignación de Listas.


Supongo que la asignación vía = de la lista a la lista distribuye las asignaciones de los elementos en una lista a los elementos en la otra lista, por eso en el segundo ejemplo $ string2 obtiene 'cosas', y por qué podemos descomprimir @_ a través de la asignación de listas.

Correcto.

He leído muchas veces que las listas no existen realmente en contexto escalar.

Esa redacción es muy ambigua. Parece que está hablando de valores de lista (que se encuentran en la memoria), pero el contexto escalar solo existe en el código (donde se encuentran los operadores).

  • Un operador de lista / coma puede evaluarse en contexto escalar.
  • No se puede devolver un valor de lista en contexto escalar.

El contexto escalar es un contexto en el cual un operador puede ser evaluado.

Un operador evaluado en contexto escalar no puede devolver una lista. Debe devolver un escalar. En términos generales, podría decirse que una lista no puede ser devuelto en contexto escalar.

Por otro lado, un operador de lista / coma se puede evaluar en contexto escalar. p.ej. scalar(4,5,6). Cada operador puede ser evaluado en cualquier contexto (aunque no necesariamente es útil hacerlo).

Pero la explicación del operador de cabras es que se trata de una asignación de lista en contexto escalar.

Eso incluye uno, sí.

Los valores de lista y los operadores de asignación de lista son dos cosas diferentes. Uno es un valor. El otro es una pieza de código.

Un operador de asignación de lista, como cualquier otro operador, se puede evaluar en contexto escalar. Un operador de asignación de lista en contexto escalar devuelve la cantidad de escalares devueltos por su RHS.

Entonces si evalúas () = qw(a b c) en contexto escalar, devolverá tres, ya que qw() colocó tres escalares en la pila.

Sin embargo, no entiendo cómo la asignación a (), una lista vacía, podría funcionar.

Al igual que la asignación ($x,$y) = qw(stuff junk things) ignora el tercer elemento devuelto por el RHS, () = qw(stuff junk things) ignora todos los elementos devueltos por el RHS.

Con mi comprensión actual, dado que las listas son inmutables, el tamaño de la lista permanecería 0

Diciendo "el tamaño de la lista permanecería cero" para ()=qw(a b c) es como decir "el valor del escalar seguirá siendo 4" para 4+5.

Para empezar, hay dudas sobre la lista de la que estás hablando. El LHS devolvió uno, el RHS devolvió uno, y el operador de asignación podría devolver uno.

El valor de la lista devuelto por el LHS será 0 en longitud.

El valor de lista devuelto por el RHS será de 3 en longitud.

En contexto escalar, el operador de asignación de listas devuelve el número de escalas devuelto por RHS (3).

En contexto de lista, el operador de asignación de listas devuelve los escalares devueltos por LHS como lvalues ​​(lista vacía).

las listas se supone que son inmutables.

Si estás pensando en términos de mutabilidad de lista, tomaste un giro equivocado en alguna parte.[2]


Notas:

  1. La llamada de documentos EXPR,EXPR,EXPR,... dos instancias de un operador binario, pero es más fácil de entender como un único operador N-ary, y en realidad se implementa como un único operador N-ary. Incluso en contexto escalar.

  2. En realidad no es cierto, pero no avancemos más en este giro equivocado.


6
2018-01-10 14:43