Lógica Jedi en Python

El siguiente post es algo técnico y probablemente sólo interesará a programadores, matemáticos y otras gentes de mal vivir. De todas maneras es un hack tan chulo que no me resisto a comentarlo. Se trata, en resumidas cuentas, de explicar el funcionamiento de las funciones lógicas and y or

Las Tablas de la Verdad

Las funciones and y or son, con diferencia, las funciones más básicas que nos permiten combinar valores booleanos. A grandes rasgos, ambas funciones toman dos valores que pueden ser True o False y devuelven un único valor (de nuevo, del tipo True o False) en función de cómo son los parámetros de entrada.

En concreto, la función and devuelve True únicamente si los dos valores que recibe son a su vez True. En cualquier otro caso devuelve False. La función or es en cierto sentido su inversa: devuelve False únicamente si los dos valores que recibe son False y devuelve True en cualquier otro caso.

Ambas funciones se pueden esquematizar y resumir con sus correspondientes tablas de la verdad:

Tabla de la verdad de la función and:

  • True and True = True
  • True and False = False
  • False and True = False
  • False and False = False

Tabla de la verdad de la función or:

  • True or True = True
  • True or False = True
  • False or True = True
  • False or False = False

Cortocircuitos

Como hemos visto en las anteriores tablas, ambas funciones tratan por igual ambos parámetros, es decir, da igual si primero les pasas uno o el otro, el resultado es independiente del orden elegido.

En la práctica, sin embargo, hay muchas ocasiones en las que el orden de los factores tiene cierta relevancia. El ejemplo más sencillo que se me ocurre sería: “si la lista tiene al menos 10 elementos y el décimo de ellos es igual a 42 haz tal”, que en Python sería algo así como:

  • if len(lista) >= 10 and lista[ 9 ]==42:
    • tal

En estos casos algunos lenguajes de programación pueden morder el polvo miserablemente si sus operadores lógicos no incorporan lo que se conoce como cortocircuitos. Concretamente, si la lista tiene menos de 10 elementos y hacemos la llamada lista[ 9 ] el programa fallará.

Hoy en día la mayoría de lenguajes son algo más inteligentes y incorporan un par de cortocircuitos en las funciones and y or. El primer cortocircuito lo lleva la función and y hace que si el primer parámetros que le pasamos es False no sea necesario evaluar el segundo. Así el código anterior nunca llegaría a ejecutar lista[ 9 ] si la lista tiene menos de 10 elementos.

De una manera similar la función or incorpora un cortocircuito que hace que si el primer parámetro es True no sea necesario evaluar el segundo.

Así, el código X and Y es equivalente al código:

  • if X:
    • if Y:
      • return True
    • else:
      • return False
  • else:
    • return False

Mientras que X or Y es equivalente a:

  • if X:
    • return True
  • else:
    • if Y:
      • return True
    • else:
      • return False

Un pequeño hack Jedi

Hay una manera un poco más compacta de expresar las funciones and y or mediante comandos if y else (cortocircuitos incluidos):

Así, X and Y sería

  • if X:
    • return Y
  • else:
    • return X

Mientras que X or Y sería

  • if X:
    • return X
  • else:
    • return Y

Ambas son implementaciones válidas y, a grandes rasgos, equivalentes a las anteriores. Sin embargo, esta nueva versión nos da pie a realizar un pequeño hack digno de un Jedi.

En Python cualquier objeto tiene un valor lógico asignado por defecto. Así, las tuplas vacías, las listas vacías, los diccionarios vacíos, el valor numérico 0, el string nulo ‘’‘’ y demás cosas que el sentido común asocia a False son, de hecho, equivalentes a False mientras que cualquier contenedor de datos no vacío, cualquier otro valor numérico o cualquier otro string se consideran True.

Esto significa que podemos usar cualquier objeto como parámetro de una función lógica (como and y or) y Python lo entenderá. Pero es que además, podemos hacer cosas como la siguiente:

print A or B

¿Y qué se supone que debe hacer Python con eso? Pues lo lógico, si A es algo que tiene contenido Python lo interpretará como True y lo imprimirá en pantalla. Si no es algo de interés imprimirá en pantalla B sea lo que sea B.

print A and B

Es algo menos intuitivo. Si A es algo que tiene contenido imprime en pantalla B (sea lo que sea) y si no imprime A (aunque seguramente no haya demasiado que imprimir).

No sé cuantos lenguajes de programación incorporan este hack en su sintaxis pero, desde luego, me parece algo muy en la linea de Python, donde las cosas simplemente funcionan todo lo bien que deberían y se comportan de la manera más lógica posible aún fuera de su radio de acción habitual.

BOLAEXTRA: La función not de Python no tiene el comportamiento peculiar de las otras dos y se limita a devolver False cuando recibe algo interpretable como True y viceversa.

Escrito en 07/03/11 10:24 por Carlos Luna en las categorías:

Comentarios

Gravatar.com se ha roto

Por ejemplo, una de las utilidades (aunque siempre vigilando) es parar implementar la operación ternaria típica de C/Java:

En java/C++ sería:
a ? b : c; //si a, realiza b, sino realiza c.

En python sería:
b if a else c # realiza b si pasa a, sino realiza c.

Por cierto, python siempre siendo más explícito que la competencia. Solo es necesario leerlo para entenderlo :)

guillem serra | 07/03/11 23:43 | #
Gravatar.com se ha roto

Este post me recuerda a algún artículo que leí (y que ahora no puedo encontrar) sobre las peculiaridades que resultaban entre la combinación de operadores lógicos y conversión de tipos en php.

Una delicia.

mada | 09/03/11 13:55 | #
Gravatar.com se ha roto

Pues si, una delicia. Es como ver un pequeño claro en todo un día nublado.

Gracias por la clara y sencilla explicación ;)

ulan | 07/04/13 08:29 | #

Deja un Comentario

Quizás quieras usar textile para dar formato a tu comentario.

"linktext":http://       _em_       *strong*       -strike-       ^sup^       ~sub~
bq. Blockquote       # Lista numerada       * Lista no-numerada       ==html crudo, sin textile==

(no será mostrado) (http://...)