• Publicidad

Adivinanza

Aprende Perl

Adivinanza

Notapor explorer » 2014-07-16 14:16 @636

Sin ejecutarlo en la terminal de línea de comandos,
¿qué es lo que sale al escribir este comando?

perl -le 'print(two + two == five ? "true" : "false")'
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Publicidad

Re: Adivinanza

Notapor BigBear » 2014-07-19 19:46 @865

La verdad traté de hacer trampa pero da error, pero sin miedo a quedar como idiota digo que da false.
BigBear
Perlero frecuente
Perlero frecuente
 
Mensajes: 981
Registrado: 2009-03-01 18:39 @818

Re: Adivinanza

Notapor explorer » 2014-07-19 20:47 @907

Yo también me equivoqué y pensé que era false, pero no concuerda con el hecho de que no sale nada en pantalla.

A ver si alguien más se anima antes de dar la solución real, que demostrará una vez más el altísimo nivel de "indecibilidad(*)" que en algunas ocasiones tiene nuestro lenguaje favorito.

Aunque otros lo llamarán "versatilidad", claro :)

(*) Aquello que no se puede explicar.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Adivinanza

Notapor explorer » 2014-07-20 14:00 @625

Sigue la traducción de la solución. El resultado es sorprendente.

«Hace unas semanas pregunté a la gente que adivine, sin probarlo antes, cuál es la salida de este programa.

perl -le 'print(two + two == five ? "true" : "false")'

(Si aún no lo ha visto antes, recomiendo que lo intente adivinar, y luego lo pruebe, antes de leer el resto del artículo).

Gente acostumbrada a Perl piensa que imprimirá true; es lo que creía. El razonamiento es como sigue: Perl está tratando a las cadenas de caracteres sin comillas two y five como cadenas de caracteres, como si estuvieran entrecomilladas, y al usar los operadores + y == con ellas, convierte las cadenas de caracteres en números, como es normal. Si las cadenas fueran como "2" y "5" Perl las trataría como 2 y 5, pero como ellas no se parecen a números decimales, Perl las interpreta como ceros. (A Perl le gustaría elevar una advertencia sobre esto, pero las advertencias no están activadas por defecto). Ya que two y five se tratan como ceros, el resultado de la comparación == es verdadera, y la cadena "true" debería seleccionarse e imprimirse.

Esto es un poco extraño, pero no excesivamente extraño; es la clase de cosas que esperarías de los lenguajes de programación, todos los cuales, más o menos, fastidian un poco. Por ejemplo, el comportamiento de Python, aunque diferente, es igualmente peculiar. Aunque Python requiere que la cadenas two y five estén entrecomilladas, es divertido ver qué ocurre con "two" + "two" == "five", que devuelve false: en Python el operador + está sobrecargado y tiene un comportamiento completamente distinto sobre cadenas de caracteres y números, así que mientras en Perl "2" + "2" es el número 4, en Python es la cadena de caracteres 22, y "two" + "two" devuelve la cadena de caracteres "twotwo". Que el programa anterior imprima true, como se supone, o incluso false, realmente no es importante.

Sin embargo, no es eso lo que el programa hace. La explicación de los dos párrafos anteriores es totalmente equivocada. En lugar de eso, el programa no imprime nada, y la razón es increíblemente complicada y extraña.

Primero, debes saber que print() tiene un primer argumento opcional. (Tengo planes para un artículo sobre cómo los argumentos primeros opcionales son casi siempre una mala idea, pero contrariamente a mi práctica habitual lo insertaré aquí). En Perl, la función print() puede ser invocada de dos formas:

print HANDLE $a, $b, $c, …;
print $a, $b, $c, …;


El primer print() saca la lista $a, $b, $c, … al identificador de archivo HANDLE. El siguiente usa el identificador por defecto, que normalmente apunta a la terminal. ¿Cómo decide Perl cuál de estas formas debe usar? Específicamente, en la segunda forma, ¿cómo sabe que $a es uno de los elementos que tiene que imprimir, en lugar de una variable que contiene un identificador del archivo en donde imprimir?

La respuesta a esta pregunta puede complicarse aún más por el hecho de que HANDLE, en la primera forma, podría ser tanto una cadena de caracteres no entrecomillada, en cuyo caso es el nombre del identificador a donde imprimir, o podría ser una variable que contiene un valor de un identificador de archivo. Los dos siguientes print() debería hacer lo mismo:

my $handle = \*STDERR;
print STDERR $a, $b, $c;
print $handle $a, $b, $c;


El método de Perl para decidir cuándo un print() en particular usa un identificador de forma explícita o el valor por defecto tiene una heurística complicada. La regla básica es que el identificador de archivo, si está presente, puede distinguirse porque se le omite una coma consecuente. Pero si el identificador de archivo fuera el resultado de una expresión arbitraria, podría ser dificultoso para el intérprete decidir donde debería ir la coma. Considere la siguiente expresión hipotética:

print $a += EXPRESIÓN, $b $c, $d, $e;

La intención aquí es que la expresión $a += EXPRESIÓN, $b calcula el valor del identificador de archivo (que se recupera desde $b, la parte $a += … solo se ejecuta por su efecto colateral) y el resto $c, $d, $e son los valores a imprimir. Permitir esta suerte de cosas sería muy confuso tanto para Perl como para el programador. Así que la siguiente regla es que la expresión del identificador de archivo, si está presente, debe ser corta, o bien una simple variable escalar, como $fh, o una cadena de caracteres simple, sin entrecomillar, que es el formato correcto para el nombre de un identificador de archivo, como HANDLE. Entonces el intérprete solo necesita mirar alrededor de un token o dos para ver si hay una coma consecuente.

Así, por ejemplo, en

print STDERR $a, $b, $c;

el print() se sigue inmediatamente por STDERR, que podría ser el nombre del identificador de archivo, y a STDERR no le sigue una coma, así que se toma STDERR por el nombre del identificador para la salida. Y en

print $x, $a, $b, $c;

el print() se sigue inmediatamente por el valor escalar simple $x, pero a esta $x le sigue una coma, así que es considerada como una de las cosas a imprimir, y el objetivo del print() es el identificador de salida por defecto.

En

print STDERR, $a, $b, $c;

Perl tiene un problema: STDERR se parece a un identificador de archivo, pero le sigue una coma. Esto es un error en tiempo de compilación. Perl se queja con un “No comma allowed after filehandle” (No se permite coma después de un identificador de archivo) y aborta. Si quiere imprimir la cadena literal STDERR, debe entrecomillarla, y si lo que quiere es imprimir $a, $b y $c hacia el identificador de archivo de salida estándar de error, debe omitir la primera coma.

Ahora volvamos al primer ejemplo.

perl -le 'print(two + two == five ? "true" : "false")'

Aquí Perl ve la cadena de caracteres no entrecomillada two, que podría ser un nombre de un identificador de archivo, y que no está seguido por una coma. Así que toma al primer two como el nombre del identificador de salida. Y después evalúa la expresión

+ two == five ? "true" : "false"

y obtiene el valor true. (El precedente + es un operador unario, que es una no-operación. Las palabras simple two y five se toman como cadenas de caracteres constantes, que comparadas con el operador numérico ==, se consideran numéricamente cero, provocando la misma advertencia que como he mencionado antes no está activada. Entonces la comparación que hace Perl es 0 == 0, que es verdadera, y el resultado es la cadena de caracteres true).

Este valor, la cadena de caracteres true, se imprime al identificador de archivo llamada two. Si hubiéramos abierto anteriormente tal identificador de archivo, como en

open two, ">", "archivo_de_salida";

entonces la salida se enviaría al identificador de archivo de la forma usual. Imprimir a identificador de archivo que no ha sido abierto provoca en Perl una advertencia opcional, pero como he mencionado, no tengo las advertencias activadas, así que print() falla silenciosamente, devolviendo un valor falso.

Teniendo activadas estas advertencias opcionales, podríamos ver una plétora de ellas:

Unquoted string "two" may clash with future reserved word at -e line 1.
Unquoted string "two" may clash with future reserved word at -e line 1.
Unquoted string "five" may clash with future reserved word at -e line 1.
Name "main::two" used only once: possible typo at -e line 1.
Argument "five" isn't numeric in numeric eq (==) at -e line 1.
Argument "two" isn't numeric in numeric eq (==) at -e line 1.
print() on unopened filehandle two at -e line 1.


(Las primeras cuatro son advertencias en tiempo de compilación; las últimas tres son problemas en tiempo de ejecución). El aviso crucial es el último, indicando que la salida del print() fue dirigida al identificador de archivo two que nunca fue abierto).

[ Añadido 20140718: Sigo acordándome de la siguiente cita de Edsger W. Dijkstra:
[Este fenómeno] toma dos apariencias diferentes: un programador coloca un programa de una línea en el escritorio de otro, y dice, "¡Adivina qué hace!" De esta observación podemos concluir que este lenguaje es una herramienta que invita a la creación de trucos intrincados; y mientras exactamente esto puede ser la explicación de lo que parece, a la vista de quienes quieren demostrar lo inteligentes que son, lo siento, pero debo considerarlo como una de las peores condenas que pueden decirse de un lenguaje de programación.

Pero mi intención es diferente de lo que describe Dijkstra. Su programador se enorgullece, pero yo estoy disgustado. De paso, creo que Dijkstra estaba hablando de APL].»
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Formación

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 0 invitados