Esta pregunta puede sonar tonta, pero ¿por qué 0
evalúa false
y cualquier otro valor [entero] true
es la mayoría de los lenguajes de programación?
Comparación de cadenas
Dado que la pregunta parece demasiado simple, me explicaré un poco más: en primer lugar, puede parecer evidente para cualquier programador, pero ¿por qué no habría un lenguaje de programación? Puede haberlo, pero no ninguno Solía: ¿dónde se 0
evalúa true
y todos los demás valores [enteros] false
? Ese comentario puede parecer aleatorio, pero tengo algunos ejemplos en los que puede haber sido una buena idea. En primer lugar, tomemos el ejemplo de la comparación de tres vías de cadenas, tomaré C strcmp
como ejemplo: cualquier programador que intente C como su primer lenguaje puede verse tentado a escribir el siguiente código:
if (strcmp(str1, str2)) { // Do something... }
Como los strcmp
retornos 0
que se evalúan false
cuando las cadenas son iguales, lo que el programador principiante intentó hacer falla miserablemente y generalmente no comprende por qué al principio. En cambio, si hubiera 0
evaluado true
esta función, podría haberse utilizado en su expresión más simple, la anterior, al comparar la igualdad, y las comprobaciones adecuadas -1
y 1
se habrían realizado solo cuando fuera necesario. Hubiéramos considerado el tipo de retorno como bool
(en nuestra opinión, quiero decir) la mayor parte del tiempo.
Además, vamos a introducir un nuevo tipo sign
, que solo toma valores -1
, 0
y 1
. Eso puede ser bastante útil. Imagine que hay un operador de nave espacial en C ++ y lo queremos std::string
(bueno, ya existe la compare
función, pero el operador de nave espacial es más divertido). La declaración actualmente sería la siguiente:
sign operator<=>(const std::string& lhs, const std::string& rhs);
Si hubiera 0
sido evaluado true
, el operador de la nave espacial ni siquiera existiría, y podríamos haber declarado de operator==
esa manera:
sign operator==(const std::string& lhs, const std::string& rhs);
Esto operator==
habría manejado la comparación de tres vías a la vez, y aún podría usarse para realizar la siguiente verificación y al mismo tiempo poder verificar qué cadena es lexicográficamente superior a la otra cuando sea necesario:
if (str1 == str2) { // Do something... }
Manejo de errores antiguos
Ahora tenemos excepciones, por lo que esta parte solo se aplica a los lenguajes antiguos donde no existe tal cosa (C por ejemplo). Si miramos la biblioteca estándar de C (y POSIX uno también), podemos ver con certeza que las funciones pueden regresar 0
cuando tienen éxito y cualquier número entero de lo contrario. Lamentablemente he visto a algunas personas hacer este tipo de cosas:
#define TRUE 0
// ...
if (some_function() == TRUE)
{
// Here, TRUE would mean success...
// Do something
}
Si pensamos en cómo pensamos en la programación, a menudo tenemos el siguiente patrón de razonamiento:
Do something
Did it work?
Yes ->
That's ok, one case to handle
No ->
Why? Many cases to handle
Si lo pensamos nuevamente, tendría sentido poner el único valor neutral 0
, a yes
(y así es como funcionan las funciones de C), mientras que todos los demás valores pueden estar allí para resolver los muchos casos de no
. Sin embargo, en todos los lenguajes de programación que conozco (excepto quizás algunos lenguajes esotéricos experimentales), que yes
evalúa false
en una if
condición, mientras que todos los no
casos evalúan true
. Hay muchas situaciones en las que "funciona" representa un caso, mientras que "no funciona" representa muchas causas probables. Si lo pensamos de esa manera, haber 0
evaluado true
y el resto false
habría tenido mucho más sentido.
Conclusión
Mi conclusión es esencialmente mi pregunta original: ¿por qué diseñamos lenguajes donde 0
está false
y los demás valores true
, teniendo en cuenta mis pocos ejemplos anteriores y tal vez algunos más en los que no pensé?
Seguimiento: Es bueno ver que hay muchas respuestas con muchas ideas y tantas razones posibles para que sea así. Me encanta lo apasionado que pareces ser al respecto. Originalmente hice esta pregunta por aburrimiento, pero como pareces tan apasionado, decidí ir un poco más allá y preguntar sobre la razón detrás de la elección booleana para 0 y 1 en Math.SE :)
fuente
strcmp()
No es un buen ejemplo para verdadero o falso, ya que devuelve 3 valores diferentes. Y se sorprenderá cuando comience a usar un shell, donde 0 significa verdadero y cualquier otra cosa significa falso.if true ; then ... ; fi
, dondetrue
es un comando que devuelve cero y esto le diceif
que se ejecute...
.bool
tipo pero las comparaciones / etc si las condiciones pueden tener cualquier valor de retorno.Respuestas:
0
esfalse
porque ambos son elementos cero en semirrelaciones comunes . Aunque son tipos de datos distintos, tiene sentido intuitivo convertirlos porque pertenecen a estructuras algebraicas isomorfas.0
es la identidad para la suma y cero para la multiplicación. Esto es cierto para números enteros y racionales, pero no para números de punto flotante IEEE-754:0.0 * NaN = NaN
y0.0 * Infinity = NaN
.false
es la identidad para Boolean xor (⊻) y cero para Boolean y (∧). Si los booleanos se representan como {0, 1} —el conjunto de enteros módulo 2— puede pensar en ⊻ como suma sin acarreo y ∧ como multiplicación.""
y[]
son identidad para concatenación, pero hay varias operaciones para las cuales tienen sentido como cero. La repetición es una, pero la repetición y la concatenación no se distribuyen, por lo que estas operaciones no forman un semired.Tales conversiones implícitas son útiles en programas pequeños, pero en general pueden hacer que los programas sean más difíciles de razonar. Solo una de las muchas compensaciones en el diseño del lenguaje.
fuente
nil
es tanto la lista vacía[]
como elfalse
valor en Common Lisp; ¿hay una tendencia a fusionar identidades de diferentes tipos de datos?) Todavía tiene que explicar por qué es natural considerar falso como identidad aditiva y verdadero como identidad multiplicativa. y no al revés. ¿No es posible considerartrue
como la identificaciónAND
y cero paraOR
?true
es también la identidad y el cero de semirrelaciones (booleanas y / o). No hay ninguna razón, aparte de la convención, para considerar quefalse
esté más cerca de 0 quetrue
.Porque las matemáticas funcionan.
Tradicionalmente, los programas C tienen condiciones como
más bien que
porque el concepto de cero es equivalente a falso es bien entendido.
fuente
Como otros han dicho, las matemáticas fueron primero. Es por eso que 0 es
false
y 1 estrue
.¿De qué matemáticas estamos hablando? Álgebras booleanas que datan de mediados de 1800, mucho antes de que aparecieran las computadoras digitales.
También se podría decir que la convención surgió de una lógica proposicional , que incluso es más antigua que las álgebras booleanas. Esta es la formalización de muchos de los resultados lógicos que los programadores conocen y aman (
false || x
igualx
,true && x
igual ,x
etc.).Básicamente estamos hablando de aritmética en un conjunto con dos elementos. Piensa en contar en binario. Las álgebras booleanas son el origen de este concepto y su base teórica. Las convenciones de lenguajes como C son solo una aplicación sencilla.
fuente
true = 1
. Eso no es del todo exacto, porquetrue != 0
no es exactamente lo mismo. Una razón (no la única) por la que uno debe evitar comparaciones comoif(something == true) { ... }
.Pensé que esto tenía que ver con la "herencia" de la electrónica, y también con el álgebra booleana, donde
0
=off
,negative
,no
,false
1
=on
,positive
,yes
,true
strcmp devuelve 0 cuando las cadenas son iguales tiene que ver con su implementación, ya que lo que realmente hace es calcular la "distancia" entre las dos cadenas. Que 0 también se considere falso es solo una coincidencia.
devolver 0 en caso de éxito tiene sentido porque 0 en este caso se usa para indicar que no hay error y cualquier otro número sería un código de error. Usar cualquier otro número para el éxito tendría menos sentido ya que solo tiene un único código de éxito, mientras que puede tener varios códigos de error. Usas "¿Funcionó?" como la expresión de instrucción if y decir 0 = sí tendría más sentido, pero la expresión es más correcta "¿Algo salió mal?" y luego ves que 0 = no tiene mucho sentido. Pensar en
false/true
esto no tiene sentido aquí, ya que en realidad lo esno error code/error code
.fuente
0
quesuccess/no error
es lo único que tiene sentido cuando otros enteros representan códigos de error. Eso0
también sucedefalse
en otros casos, en realidad no importa, ya que no estamos hablando de verdadero o falso aquí;)strcmp()
cálculo de la distancia es bastante bueno. Si se hubiera llamadostrdiff()
,if (!strdiff())
sería muy lógico.Como se explica en este artículo , los valores
false
ytrue
no deben confundirse con los enteros 0 y 1, sino que pueden identificarse con los elementos del campo de Galois (campo finito) de dos elementos (ver aquí ).Un campo es un conjunto con dos operaciones que satisfacen ciertos axiomas.
Los símbolos 0 y 1 se usan convencionalmente para denotar las identidades aditiva y multiplicativa de un campo porque los números reales también son un campo (pero no finito) cuyas identidades son los números 0 y 1.
La identidad aditiva es el elemento 0 del campo, de modo que para todas las x:
y la identidad multiplicativa es el elemento 1 del campo, de modo que para todo x:
El campo finito de dos elementos tiene solo estos dos elementos, a saber, la identidad aditiva 0 (o
false
) y la identidad multiplicativa 1 (otrue
). Las dos operaciones de este campo son el XOR lógico (+) y el AND lógico (*).Nota. Si voltea las operaciones (XOR es la multiplicación y AND es la suma), entonces la multiplicación no es distributiva sobre la suma y ya no tiene un campo. En tal caso, no tiene ninguna razón para llamar a los dos elementos 0 y 1 (en cualquier orden). Tenga en cuenta también que no puede elegir la operación OR en lugar de XOR: no importa cómo interprete OR / AND como suma / multiplicación, la estructura resultante no es un campo (no todos los elementos inversos existen como lo requieren los axiomas del campo).
En cuanto a las funciones C:
strcmp
calcula la diferencia entre dos cadenas. 0 significa que no hay diferencia entre dos cadenas, es decir, que dos cadenas son iguales.Las explicaciones intuitivas anteriores pueden ayudar a recordar la interpretación de los valores de retorno, pero es aún más fácil simplemente verificar la documentación de la biblioteca.
fuente
Debe considerar que los sistemas alternativos también pueden ser decisiones de diseño aceptables.
Shells: el estado de salida 0 es verdadero, el no cero es falso
Ya se ha mencionado el ejemplo de shells que tratan un estado de salida 0 como verdadero.
La razón es que hay una forma de tener éxito, pero muchas formas de fracasar, por lo que usar 0 como valor especial que significa "sin errores" es pragmático.
Ruby: 0 es como cualquier otro número
Entre los lenguajes de programación "normales", hay algunos valores atípicos, como Ruby, que tratan el 0 como un valor verdadero.
La razón es que solo eso
false
ynil
debe ser falso. Para muchos novatos de Ruby, es una trampa. Sin embargo, en algunos casos, es bueno que 0 se trate como cualquier otro número.Sin embargo, dicho sistema solo funciona en un lenguaje que puede distinguir booleanos como un tipo separado de los números. En los primeros días de la informática, los programadores que trabajaban con lenguaje ensamblador o lenguaje de máquina sin procesar no tenían tales lujos. Probablemente sea natural tratar a 0 como el estado "en blanco" y establecer un bit a 1 como un indicador cuando el código detecta que algo sucedió. Por extensión, la convención desarrolló que el cero se trataba como falso, y los valores distintos de cero se consideraron verdaderos. Sin embargo, no tiene por qué ser así.
Java: los números no pueden tratarse como booleanos en absoluto
En Java,
true
yfalse
son los únicos valores booleanos. Los números no son booleanos, y ni siquiera se pueden convertir en booleanos ( especificación del lenguaje Java, sección 4.2.2 ):Esa regla simplemente evita la pregunta por completo: todas las expresiones booleanas deben escribirse explícitamente en el código.
fuente
if (thing === 0)
, eso no es genial.0
seatrue
(como cualquier otro número entero) en un lenguaje dinámico. A veces me encontraba atrapando un0
cuando intentaba atraparNone
en Python, y eso a veces puede ser bastante difícil de detectar.if [ 0 ] ; then echo this executes ; fi
. El valor de datos falsos es una cadena vacía, y una falsedad comprobable es un estado de terminación fallido de un comando, que está representado por un valor distinto de cero.Antes de abordar el caso general, podemos analizar sus contraejemplos.
Comparaciones de cadenas
Lo mismo vale para muchos tipos de comparaciones, en realidad. Tales comparaciones calculan una distancia entre dos objetos. Cuando los objetos son iguales, la distancia es mínima. Entonces, cuando la "comparación tiene éxito", el valor es 0. Pero en realidad, el valor de retorno de no
strcmp
es un valor booleano, es una distancia, y eso es lo que atrapa a los programadores inconscientes .if (strcmp(...)) do_when_equal() else do_when_not_equal()
En C ++ podríamos rediseñar
strcmp
para devolver unDistance
objeto, que anulaoperator bool()
para devolver verdadero cuando 0 (pero luego sería mordido por un conjunto diferente de problemas). O en C simple, solo tiene unastreq
función que devuelve 1 cuando las cadenas son iguales, y 0 en caso contrario.Llamadas API / código de salida del programa
Aquí te importa la razón por la que algo salió mal, porque esto impulsará las decisiones por error. Cuando las cosas tienen éxito, no quieres saber nada en particular: tu intención se realiza. Por lo tanto, el valor de retorno debe transmitir esta información. Es no un valor lógico, es un código de error. El valor de error especial 0 significa "sin error". El resto del rango representa errores localmente significativos con los que tiene que lidiar (incluido 1, que a menudo significa "error no especificado").
Caso general
Esto nos deja con la pregunta: ¿por qué son los valores booleanos
True
yFalse
comúnmente representado con 1 y 0, respectivamente?Bueno, además del argumento subjetivo "se siente mejor de esta manera", aquí hay algunas razones (subjetivas también) en las que puedo pensar:
analogía del circuito eléctrico. La corriente está encendida durante 1s y apagada durante 0s. Me gusta tener (1, Sí, Verdadero, Encendido) juntos y (0, No, Falso, Apagado), en lugar de otra combinación
Inicializaciones de memoria. Cuando tengo
memset(0)
un montón de variables (ya sean ints, flotantes, bools), quiero que su valor coincida con los supuestos más conservadores. Por ejemplo, mi suma es inicialmente 0, el predicado es falso, etc.Quizás todas estas razones están ligadas a mi educación: si me hubieran enseñado a asociar 0 con Verdadero desde el principio, optaría por lo contrario.
fuente
bool
en absoluto.int
quebool
en muchos lenguajes de programación. La comparación y el material de gestión de errores son solo ejemplos de lugares donde tendría sentido emitirlo de una manera diferente a la que se hace actualmente.Desde una perspectiva de alto nivel, estás hablando de tres tipos de datos bastante diferentes:
Un booleano. La convención matemática en álgebra booleana es usar 0 para
false
y 1 paratrue
, por lo que tiene sentido seguir esa convención. Creo que de esta manera también tiene más sentido intuitivamente.El resultado de la comparación. Esto tiene tres valores:
<
,=
y>
(aviso de que ninguno de ellos estrue
). Para ellos tiene sentido usar los valores de -1, 0 y 1, respectivamente (o, más generalmente, un valor negativo, cero y un valor positivo).Si desea verificar la igualdad y solo tiene una función que realiza una comparación general, creo que debería hacerlo explícito utilizando algo como
strcmp(str1, str2) == 0
. Me resulta!
confuso usarlo en esta situación, porque trata un valor no booleano como si fuera un valor booleano.Además, tenga en cuenta que la comparación y la igualdad no tienen que ser lo mismo. Por ejemplo, si ordena a las personas por su fecha de nacimiento,
Compare(me, myTwin)
debería regresar0
, peroEquals(me, myTwin)
debería regresarfalse
.El éxito o el fracaso de una función, posiblemente también con detalles sobre ese éxito o fracaso. Si está hablando de Windows, entonces se llama a este tipo
HRESULT
y un valor distinto de cero no necesariamente indica falla. De hecho, un valor negativo indica falla y éxito no negativo. El valor de éxito es muy frecuenteS_OK = 0
, pero también puede ser, por ejemploS_FALSE = 1
, u otros valores.La confusión proviene del hecho de que tres tipos de datos lógicamente diferentes se representan realmente como un tipo de datos único (un entero) en C y algunos otros lenguajes y que puede usar un entero en una condición. Pero no creo que tenga sentido redefinir booleano para simplificar el uso de algunos tipos no booleanos en condiciones.
Además, considere otro tipo que a menudo se usa en una condición en C: un puntero. Allí, es natural tratar un
NULL
puntero (que se representa como0
) comofalse
. Por lo tanto, seguir su sugerencia también dificultaría el trabajo con punteros. (Aunque, personalmente, prefiero comparar explícitamente los punteros conNULL
, en lugar de tratarlos como booleanos).fuente
Cero puede ser falso porque la mayoría de las CPU tienen un indicador CERO que se puede utilizar para bifurcar. Guarda una operación de comparación.
Veamos por qué.
Algunos psuedocode, ya que la audiencia probablemente no lee el ensamblado
c-source bucle simple llamadas wibble 10 veces
algunos pretenden asamblea para eso
c- fuente otro bucle simple llama wibble 10 veces
alguna asamblea simulada para este caso
alguna fuente más c
y la asamblea
¿Ves lo corto que es eso?
alguna fuente más c
y el ensamblaje (supongamos un compilador marginalmente inteligente que puede reemplazar == 0 sin comparar)
Ahora intentemos una convención de verdadero = 1
algo más c source #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()
y la asamblea
¿Ves cuán corto es el caso con non-zero true?
Las primeras CPU tenían pequeños conjuntos de banderas unidas al acumulador.
Para verificar si a> bo a = b generalmente toma una instrucción de comparación.
Vamos a repetir esto. En algunas CPU antiguas no tenía que usar una instrucción de comparación para el acumulador igual a CERO, o un acumulador menor que cero.
¿Ahora ves por qué cero podría ser falso?
Tenga en cuenta que este es el código psuedo y ningún conjunto de instrucciones real se parece a esto. Si conoces el ensamblaje, sabes que estoy simplificando mucho las cosas aquí. Si sabes algo sobre el diseño del compilador, no necesitas leer esta respuesta. Cualquiera que sepa algo sobre el desenrollamiento de bucles o la predicción de ramificaciones, la clase avanzada está al final del pasillo en la habitación 203.
fuente
if (foo)
yif (foo != 0)
debería generar el mismo código, y en segundo lugar, que está mostrando que el lenguaje ensamblador que está utilizando, de hecho, tiene operandos booleanos explícitas y ensayos para ellos. Por ejemplojz
significajump if zero
. En otras palabrasif (a == 0) goto target;
. Y la cantidad ni siquiera se está probando directamente; la condición se convierte en un indicador booleano que se almacena en una palabra de máquina especial. En realidad es más comocpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
jz
instrucción pero nojnz
? (o cualquier otro conjunto asimétrico de instrucciones condicionales)Hay muchas respuestas que sugieren que la correspondencia entre 1 y verdadero es necesaria por alguna propiedad matemática. No puedo encontrar ninguna de esas propiedades y sugerir que es una convención puramente histórica.
Dado un campo con dos elementos, tenemos dos operaciones: suma y multiplicación. Podemos mapear operaciones booleanas en este campo de dos maneras:
Tradicionalmente, identificamos True con 1 y False con 0. Identificamos AND con * y XOR con +. Por lo tanto, OR es una adición saturante.
Sin embargo, podríamos identificar fácilmente Verdadero con 0 y Falso con 1. Luego identificamos OR con * y XNOR con +. Por lo tanto, AND es una adición saturante.
fuente
Curiosamente, cero no siempre es falso.
En particular, la convención de Unix y Posix es definir
EXIT_SUCCESS
como 0 (yEXIT_FAILURE
como 1). En realidad, ¡es incluso una convención estándar de C !Entonces, para shells de Posix y syscalls de salida (2) , 0 significa "exitoso", que intuitivamente es más verdadero que falso.
En particular, el shell
if
quiere un retorno del procesoEXIT_SUCCESS
(que es 0) para seguir su rama "entonces".En Scheme (pero no en Common Lisp o en MELT ) 0 y nil (es decir,
()
en Scheme) son verdaderos, ya que el único valor falso es#f
Estoy de acuerdo, ¡estoy jugando!
fuente
C se utiliza para la programación de bajo nivel cerca del hardware, un área en la que a veces es necesario cambiar entre operaciones lógicas y bit a bit en los mismos datos. La obligación de convertir una expresión numérica a booleana solo para realizar una prueba desordenaría el código.
Puedes escribir cosas como:
más bien que
En un ejemplo aislado, no es tan malo, pero tener que hacerlo será molesto.
Asimismo, conversar operaciones. Es útil para el resultado de una operación booleana, como una comparación, producir un 0 o 1: supongamos que queremos establecer el tercer bit de alguna palabra en función de si
modemctrl
el portador detecta el bit:Aquí tenemos que tener
!= 0
, para reducir el resultado de la&
expresión biwise a0
o1
, pero debido a que el resultado es solo un número entero, nos ahorramos tener que agregar algo de molesto elenco para convertir aún más el valor booleano en número entero.A pesar de que la C moderna ahora tiene un
bool
tipo, aún conserva la validez de un código como este, tanto porque es algo bueno como por la rotura masiva con compatibilidad con versiones anteriores que se causaría de otra manera.Otro ejemplo donde C es resbaladizo: probar dos condiciones booleanas como un interruptor de cuatro vías:
¡No podrías quitarle esto al programador C sin luchar!
Por último, C a veces sirve como una especie de lenguaje ensamblador de alto nivel. En lenguajes ensambladores, tampoco tenemos tipos booleanos. Un valor booleano es solo un bit o un cero frente a un valor distinto de cero en una ubicación o registro de memoria. Un número entero cero, cero booleano y la dirección cero se prueban de la misma manera en conjuntos de instrucciones en lenguaje ensamblador (y quizás incluso en coma flotante cero). La semejanza entre C y el lenguaje ensamblador es útil, por ejemplo, cuando C se usa como el idioma de destino para compilar otro idioma (¡incluso uno que ha marcado booleanos!)
fuente
Un valor booleano o de verdad solo tiene 2 valores. Verdadero y falso.
Estos no deben representarse como enteros, sino como bits (0 y 1).
Decir que cualquier otro número entero al lado de 0 o 1 no es falso es una declaración confusa. Las tablas de verdad tratan con valores de verdad, no enteros.
Desde un valor de verdad prospectivo, -1 o 2 romperían todas las tablas de verdad y cualquier lógica booleana asociada con ellas.
La mayoría de los idiomas generalmente tienen un
boolean
tipo que cuando se convierte a un tipo de número como entero revela falso para ser lanzado como un valor entero de 0.fuente
TRUE
oFALSE
. Nunca dije, tal vez lo hice, pero no fue intencionado, los enteros eran verdaderos o falsos, pregunté por qué evalúan a cualquiera cuando se convierten en booleanos.En última instancia, está hablando de romper el lenguaje principal porque algunas API son malas. Las API de mierda no son nuevas, y no puede solucionarlas rompiendo el idioma. Es un hecho matemático que 0 es falso y 1 es verdadero, y cualquier lenguaje que no respete esto está fundamentalmente roto. La comparación de tres vías es un nicho y no tiene nada que ver con que su resultado se convierta implícitamente,
bool
ya que arroja tres resultados posibles. Las antiguas API de C simplemente tienen un manejo de errores terrible y también están limitadas porque C no tiene las características de lenguaje necesarias para no tener interfaces terribles.Tenga en cuenta que no estoy diciendo eso para los idiomas que no tienen conversión integer-> booleana implícita.
fuente
+
símbolo denota OR. Así, por ejemplo,abc + a'b'c
significa(a and b and c) or (a and (not b) and (not c))
.