6

La documentación oficial de la función unlink dice que será false si hay algún error, pero cuando intento probar su valor para ver si es falso, el compilador me odia:

#!/usr/bin/perl

use utf8;
use strict;
use warnings;

my $fichero = "una_prueba";

if (system("touch", $fichero) != 0) {
    die "No se pudo crear $fichero (estatus $?)";
}

print "Fichero $fichero creado.\n";

if (unlink($fichero) == false) {
    die "No se pudo remover $fichero: $!";
}

print "Fichero $fichero removido.\n";

exit(0);

Cuando intento compilar ese programa, el compilador me da este error y no hace nada:

Bareword "false" not allowed while "strict subs" in use at ejemplo-falso line 15.
Execution of ejemplo-falso aborted due to compilation errors.

Pensando que quizás tenía que marcar el valor como cadena, lo cambié así:

if (unlink($fichero) == "false") {

Cuando hice eso, esta vez el programa sí pudo ejecutarse, pero con esta queja misteriosa:

Fichero una_prueba creado.
Argument "false" isn't numeric in numeric eq (==) at ejemplo-falso line 15.
Fichero una_prueba removido.

Dado que funcionó correctamente, ¿hay realmente algún problema con escribirlo así?

¿Se debe hacer caso a esa advertencia ahí — o no?

En efecto, la pregunta fundamental aquí es ¿cómo se debe (o no se debe) comparar un valor booleano en Perl?


Esta pregunta fue inspirada por esta pregunta de SO y también por esta pregunta aquí. Prefiero esperar a que la conteste la comunidad aquí en vez de dejar una respuesta mía.

tchrist
  • 503
  • 6
  • 19

2 Answers2

6

Cada expresión en Perl puede ser evaluado en un contexto booleano.

if ( **alguna expresión** ) {
    # Caso verdadero
} else {
    # Caso falso
}

Entonces, en tu ejemplo:

if ( unlink($fichero) ) {
    print "Fichero $fichero removido.\n";
} else {
    die "No se pudo remover $fichero: $!";
}

O si solo quieres probar el caso falso, puedes negar la expresión así:

if ( ! unlink($fichero) ) {
    die "No se pudo remover $fichero: $!";
}

Hay otras formas también; puedes elegir según tu preferencia y contexto:

unlink($fichero) or die "No se pudo removar $fichero: $!";

En este caso, Perl va a ejecutar lo a la derecha de or, solo si lo a la izquierda se evalúa como falso. (Si quieres ejecutarlo solo en el caso verdadero, cambié el or para un and). Una nota: puedes usar || en vez de or y && en vez de and.

También hay el operador ternario:

unlink($fichero)
    ? print "Fichero $fichero removido.\n"
    : die "No se pudo remover $fichero: $!";

En todos los casos, Perl decide cuales expresiones son "verdadero" y "falso" según unas reglas que pueden ser simplificados así. Los siguientes valores son falsos. Lo demás son verdaderos:

  • undef
  • 0 (el número cero)
  • "" (el string vacío)
  • "0" (el string que contiene sólo "cero")
  • Cualquier objeto blessed que sobrecarga conversión booleano para retornar falso
  • Listas y hashes vacías

Y con esa información, podemos razonar otros caso comunes:

  • "0.0" es verdadero
  • "0.0"+0 es falso, porque está evaluado en contexto numérico, y así la resulta es 0 que es falso)
  • 2-3+1 es falso, porque es una expresión matemática que resulta en 0
jachguate
  • 25,659
  • 7
  • 35
  • 61
Flimzy
  • 1,924
  • 10
  • 32
  • Bien dicho. Quisiera añadir que (se puede decir que) en Perl existe otro valor falso que no ha mencionado nadie: el valor que viene de los operadores comparativos como `(0 > 1)`. Es una especie de falso que vale `""` como cadena y 0 como número, pero no provoca advertencias cuando se usa como número como sí tendrías si fuera `undef`. A veces este valor especial se llama ***falso mágico***, o sea *magic false* en inglés. – tchrist Dec 08 '15 at 10:58
  • @tchrist: Hablas del [dualvar](http://perldoc.perl.org/Scalar/Util.html#dualvar)? Pensé mencionarlo, pero los dos valores de un dualvar(0,"") son falsos, entonces no cambia esa respuesta. Así no estaba seguro si fue prudente mencionar ese detalle. Pero puedo añadir eso si piensas que será valioso. – Flimzy Dec 08 '15 at 11:07
  • No del dualvar específicamente. Hablo del `PL_sv_no` que puedes encontrar o bien en el código C de perl, o bien en la documentación de [perlapi](http://perldoc.perl.org/perlapi.html). Si decompilas un programa perl que usa este valor, por ejemplo con el comando `perl -MO=Concise,-exec -e 'print 0 > 1'`, verás que lo llama `sv_no` allí (porque el decompilador siempre quita el prefijo `PL_`). – tchrist Dec 08 '15 at 11:18
  • @tchrist: Creo que no soy tan experto para elaborar en este nivel de los internos de Perl. Si quieres añadir esa información a la respuesta, adelante... – Flimzy Dec 08 '15 at 11:19
2

Perl no tiene un tipo de dato booleano como otros lenguajes y por ello no tiene palabras reservadas (keywords) para tal fin, tales como false o true.

Sin embargo evalúa expresiones booleanas todo el tiempo para la toma de decisiones.

En un if, cualquier expresión que retorne 0, '' o undef se toma como falso y el resto se toma como verdadero (ver otros casos de falso abajo).

Para solucionar tu problema, deberías modificar el bloque if de la siguiente manera:

if (!unlink($fichero)) { 
   die "No se pudo remover $fichero: $!";
} 

Personalmente, yo cambiaría también los if por expresiones que incluyan los operadores and y or. Que en la practica equivalen a if y else respectivamente.

# si es !== 0 (verdadero), evalua el die.
system("touch", $fichero) && die "No se pudo crear $fichero (estátus $?)";

print "Fichero $fichero creado.\n";

# si es falso, evalua el die
unlink($fichero) || die "No se pudo remover $fichero: $!";

Mas casos que retornan falso:

  • No dejes de ver la respuesta @Flimzy, incluye muy buenas notas sobre otros casos donde puede resultar en falso.

  • Existe otro valor falso: el valor que viene de operadores comparativos como (0 > 1). Es una especie de falso que vale "" como cadena y 0 como número, pero no provoca advertencias cuando se usa como número como sí tendrías si fuera undef. Ese valor especial se llama falso mágico de vez en cuando. (*)

Segun este post en stack overflow, hay otras variantes que también se consideran como un falso en condicionales:

0
'0'
undef
''  # Empty scalar
()  # Empty list
('')
rnrneverdies
  • 16,491
  • 3
  • 49
  • 79