Valores booleanos canónicos
Lo que te está pasando aquí es que el valor interno que usa Perl para representar un falso canónico es un valor que ya tiene sentido numérico, mientras la cadena de cero caracteres no lo tiene.
He aquí la diferencia entre los dos valores que usaste en cuanto a sus representaciones internas:
tchrist% perl -MDevel::Peek -e 'Dump("")'
SV = PV(0x7ff4aa804080) at 0x7ff4aa8292d0
REFCNT = 1
FLAGS = (POK,IsCOW,READONLY,PROTECT,pPOK)
PV = 0x7ff4aa405230 ""\0
CUR = 0
LEN = 10
COW_REFCNT = 0
tchrist% perl -MDevel::Peek -e 'Dump(! "true")'
SV = PVNV(0x7fa48c0021f0) at 0x104ba29e8
REFCNT = 2147483647
FLAGS = (IOK,NOK,POK,READONLY,PROTECT,pIOK,pNOK,pPOK)
IV = 0
NV = 0
PV = 0x104b887f5 ""
CUR = 0
LEN = 0
Cuando haces $x + 0
, la operación aritmética de +
intenta extraer un valor numérico (NV, o en algunos casos IV) del valor escalar (SV). Como ves en las pruebas que hice arriba, la variable $x
es una cadena que no tiene ni NV ni IV. El algoritmo que usa Perl para inferir un número de una cadena (PV, valor puntero) emite la queja que viste sobre un argumento no-numérico para la adición.
Por otro lado, cuando hiciste lo mismo con $y + 0
, la forma interna de la variable $y
no era un PV (cadena) sino un PVNV, o sea un PV y un NV a la vez. Porque este PVNV sí tiene valor numérico NV = 0
, no sale ninguna advertencia cuando intentas hacer una operación numérica con él.
Si te fijas en los detalles de Devel::Peek
, quizás verás alguna cosa más. Las direcciones de los PV no se parecen en absoluto, siendo el uno 0x7ff4aa405230 (grande) y el otro 0x104b887f5 (chico). Ocurre que Perl almacenan esos dos punteros en dos espacios completamente distintos porque el uno es un valor fundamental del compilador mismo y el otro ni es más que uno que calculaste tú.
Además el REFCNT (el número de referencias) del primero es 1 solo, pero el REFCNT del segundo es un número enorme, de hecho 0x7FFF_FFFF.
Falso mágico
Esta curiosidad puede explicarse por la existencia de un valor especial que Perl mantiene para representar un falso canónico. En la documentación perlapi para programadores C, explica lo siguiente:
This is the false SV
. See PL_sv_yes
. Always refer to this as &PL_sv_no
.
SV PL_sv_no
Traduciendo libremente,
Este es el SV
falso. Véase PL_sv_yes
. Siempre se refiere a este valor como &PL_sv_no
.
SV PL_sv_no
A veces se llama este falso canónico falso mágico (magic false en inglés) por el hecho de que funcione como undef
pero sin las advertencias que provocarías si usases undef
para eso.
Se puede obtener otra muestra de este falso mágico por decompilar un programa mínimo:
tchrist% perl -MO=Concise,-exec -e 'print 0 > 1'
1 <0> enter
2 <;> nextstate(main 1 -e:1) v:{
3 <0> pushmark s
4 <$> const(SPECIAL sv_no) s/FOLD
5 <@> print vK
6 <@> leave[1 ref] vKP/REFC
-e syntax OK
Este falso canónico se produce en varios escenarios:
- Los operadores booleanos come
! 1
, ! "true"
.
- Los operadores relacionales como
<
, >
, &c.
- Una sustitución que no substituye nada
"foo" =~ s/x/y/gr
Cómo usarlo
Al fin y al cabo, lo importante que quiero que entiendas es que tú puedes aprovechar del falso mágico si quisieras tener un valor en tu programa que evaluase a 0 como número y a ""
como cadena, pero no quieres las advertencias que siempre salen al usar undef
así. Por ejemplo:
use utf8;
my $falso_mágico = (0 > 1);
O igualmente:
use utf8;
my $falso_mágico = !0;
O aun:
use utf8;
my $falso_mágico = ! "true";
Hazme el favor de nunca escribir esto:
use utf8;
my $falso_mágico = ! "false"; # ¡qué malo soy!
Sí, es verdad que esa versión produce el mismo valor, pero ese código va a confundir a todo el mundo. Por favor no lo hagas. :)
Pregunta relacionada: ¿Cómo puedo probar si un valor booleano es falso en Perl?