Essa pergunta pode parecer idiota, mas por que 0
avalia false
e qualquer outro valor [inteiro] para a true
maioria das linguagens de programação?
Comparação de strings
Como a pergunta parece um pouco simples demais, vou me explicar um pouco mais: antes de tudo, pode parecer evidente para qualquer programador, mas por que não haveria uma linguagem de programação - pode realmente haver, mas não nenhuma Eu usei - para onde 0
avalia true
e todos os outros valores [inteiros] para false
? Essa observação pode parecer aleatória, mas tenho alguns exemplos em que pode ter sido uma boa ideia. Primeiro de tudo, vamos dar o exemplo de comparação tríplice de strings, vou usar C strcmp
como exemplo: qualquer programador que tente C como sua primeira língua pode ser tentado a escrever o seguinte código:
if (strcmp(str1, str2)) { // Do something... }
Como strcmp
retornos 0
que avaliam false
quando as strings são iguais, o que o programador iniciante tentou fazer falha miseravelmente e ele geralmente não entende o porquê. Em vez disso, se tivesse 0
avaliado true
, essa função poderia ter sido usada em sua expressão mais simples - a acima - ao comparar a igualdade, e as verificações apropriadas -1
e 1
teriam sido realizadas somente quando necessário. Teríamos considerado o tipo de retorno como bool
(na minha opinião, quero dizer) na maioria das vezes.
Além disso, vamos apresentar um novo tipo sign
,, que apenas aceita valores -1
, 0
e 1
. Isso pode ser bastante útil. Imagine que existe um operador de espaçonave em C ++ e queremos isso std::string
(bem, já existe a compare
função, mas o operador de espaçonave é mais divertido). A declaração seria atualmente a seguinte:
sign operator<=>(const std::string& lhs, const std::string& rhs);
Se tivesse 0
sido avaliado true
, o operador da nave espacial nem existiria, e poderíamos ter declarado operator==
assim:
sign operator==(const std::string& lhs, const std::string& rhs);
Isso operator==
teria tratado a comparação de três maneiras ao mesmo tempo e ainda poderia ser usado para executar a verificação a seguir, enquanto ainda é possível verificar qual sequência é lexicograficamente superior à outra quando necessário:
if (str1 == str2) { // Do something... }
Tratamento de erros antigos
Agora temos exceções, portanto essa parte se aplica apenas aos idiomas antigos onde não existe (C por exemplo). Se olharmos para a biblioteca padrão de C (e também para a POSIX), podemos ver com certeza que muitas funções retornam 0
quando bem-sucedidas e qualquer número inteiro caso contrário. Infelizmente, tenho visto algumas pessoas fazerem esse tipo de coisa:
#define TRUE 0
// ...
if (some_function() == TRUE)
{
// Here, TRUE would mean success...
// Do something
}
Se pensarmos em como pensamos em programação, geralmente temos o seguinte padrão de raciocínio:
Do something
Did it work?
Yes ->
That's ok, one case to handle
No ->
Why? Many cases to handle
Se pensarmos sobre isso de novo, teria sentido colocar o único valor neutro,, 0
para yes
(e é assim que as funções de C funcionam), enquanto todos os outros valores podem estar lá para resolver os muitos casos do no
. No entanto, em todas as linguagens de programação que conheço (exceto talvez algumas linguagens esotéricas experimentais), isso é yes
avaliado false
em uma if
condição, enquanto todos os no
casos são avaliados em true
. Existem muitas situações em que "funciona" representa um caso, enquanto "não funciona" representa muitas causas prováveis. Se pensarmos dessa maneira, ter 0
avaliado true
e o resto false
teria feito muito mais sentido.
Conclusão
Minha conclusão é essencialmente minha pergunta original: por que projetamos linguagens onde 0
estão false
e outros valores true
, levando em conta meus poucos exemplos acima e talvez mais alguns em que eu não pensava?
Acompanhamento: É bom ver que existem muitas respostas com muitas idéias e o maior número possível de razões para que seja assim. Eu amo como você parece apaixonado por isso. Eu originalmente fiz essa pergunta por tédio, mas como você parece tão apaixonado, decidi ir um pouco mais longe e perguntar sobre a lógica por trás da escolha booleana de 0 e 1 em Math.SE :)
fonte
strcmp()
não é um bom exemplo para true ou false, pois retorna 3 valores diferentes. E você ficará surpreso quando começar a usar um shell, onde 0 significa verdadeiro e qualquer outra coisa significa falso.if true ; then ... ; fi
, ondetrue
está um comando que retorna zero e indicaif
para executar...
.bool
tipo, mas comparações / se as condições etc. pode ter qualquer valor de retorno.Respostas:
0
éfalse
porque ambos são zero elementos em semirriscos comuns . Mesmo sendo tipos de dados distintos, faz sentido intuitivo converter entre eles porque pertencem a estruturas algébricas isomórficas.0
é a identidade para adição e zero para multiplicação. Isso vale para números inteiros e racionais, mas não para números de ponto flutuante IEEE-754:0.0 * NaN = NaN
e0.0 * Infinity = NaN
.false
é a identidade para booleano xor (⊻) e zero para booleano e (∧). Se os booleanos são representados como {0, 1} - o conjunto de números inteiros módulo 2 - você pode pensar em ⊻ como adição sem transporte e ∧ como multiplicação.""
e[]
são identidades para concatenação, mas existem várias operações para as quais elas fazem sentido como zero. A repetição é uma, mas a repetição e a concatenação não se distribuem; portanto, essas operações não formam um semicondutor.Tais conversões implícitas são úteis em pequenos programas, mas, em geral, podem dificultar a discussão de programas. Apenas uma das muitas desvantagens no design de idiomas.
fonte
nil
existe a lista vazia[]
e ofalse
valor no Common Lisp; existe uma tendência de mesclar identidades de diferentes tipos de dados?) Você ainda precisa explicar por que é natural considerar falso como uma identidade aditiva e verdadeiro como uma identidade multiplicativa e não o contrário. Não é possível considerartrue
como identificarAND
e zero paraOR
?true
é também a identidade e o zero de semirriscos (booleano e / ou). Segundo a convenção, não há razão para considerar quefalse
seja mais próximo de 0 quetrue
.Porque a matemática funciona.
Tradicionalmente, os programas C têm condições como
ao invés de
porque o conceito de zero sendo equivalente a falso é bem compreendido.
fonte
Como outros já disseram, a matemática veio primeiro. É por isso que 0 é
false
e 1 étrue
.De que matemática estamos falando? Álgebras booleanas que datam de meados do século XIX, muito antes do surgimento dos computadores digitais.
Você também pode dizer que a convenção surgiu da lógica proposicional , que é ainda mais antiga que as álgebras booleanas. Essa é a formalização de muitos dos resultados lógicos que os programadores conhecem e amam (
false || x
iguaisx
,true && x
iguaisx
e assim por diante).Basicamente, estamos falando de aritmética em um conjunto com dois elementos. Pense em contar em binário. As álgebras booleanas são a origem desse conceito e sua base teórica. As convenções de idiomas como C são apenas uma aplicação direta.
fonte
true = 1
. Isso não é exatamente exato, porque otrue != 0
que não é exatamente o mesmo. Uma razão (e não a única) pela qual se deve evitar comparaçõesif(something == true) { ... }
.Eu pensei que isso tinha a ver com a "herança" da eletrônica e também da álgebra booleana, onde
0
=off
,negative
,no
,false
1
=on
,positive
,yes
,true
strcmp retorna 0 quando as strings são iguais tem a ver com sua implementação, pois o que realmente faz é calcular a "distância" entre as duas strings. Que 0 também seja considerado falso é apenas uma coincidência.
retornar 0 com êxito faz sentido, porque 0, nesse caso, é usado para significar nenhum erro e qualquer outro número seria um código de erro. Usar qualquer outro número para obter sucesso faria menos sentido, pois você possui apenas um código de sucesso, enquanto você pode ter vários códigos de erro. Você usa "Funcionou?" como a expressão if e diga 0 = yes, faria mais sentido, mas a expressão é mais correta "Alguma coisa deu errado?" e então você vê que 0 = não faz muito sentido. Pensar
false/true
realmente não faz sentido aqui, como é realmenteno error code/error code
.fonte
0
parasuccess/no error
é a única coisa que faz sentido quando outras inteiros representam os códigos de erro. Isso0
também acontece para representarfalse
em outros casos realmente não importa, já que não estamos a falar de verdadeiro ou falso aqui em tudo;)strcmp()
cálculo da distância é bastante bom. Se tivesse sido chamadostrdiff()
,if (!strdiff())
seria muito lógico.Conforme explicado neste artigo , os valores
false
etrue
não devem ser confundidos com os inteiros 0 e 1, mas podem ser identificados com os elementos do campo Galois (campo finito) de dois elementos (veja aqui ).Um campo é um conjunto com duas operações que satisfazem certos axiomas.
Os símbolos 0 e 1 são convencionalmente usados para denotar as identidades aditiva e multiplicativa de um campo porque os números reais também são um campo (mas não finito) cujas identidades são os números 0 e 1.
A identidade aditiva é o elemento 0 do campo, de modo que para todos os x:
e a identidade multiplicativa é o elemento 1 do campo, de modo que para todos os x:
O campo finito de dois elementos possui apenas esses dois elementos, a identidade aditiva 0 (ou
false
) e a identidade multiplicativa 1 (outrue
). As duas operações desse campo são o XOR lógico (+) e o AND lógico (*).Nota. Se você inverte as operações (XOR é a multiplicação e AND é a adição), a multiplicação não é distributiva sobre a adição e você não tem mais um campo. Nesse caso, você não tem motivos para chamar os dois elementos 0 e 1 (em qualquer ordem). Observe também que você não pode escolher a operação OR em vez de XOR: não importa como você interpreta OR / AND como adição / multiplicação, a estrutura resultante não é um campo (nem todos os elementos inversos existem conforme exigido pelos axiomas do campo).
Em relação às funções C:
strcmp
calcula a diferença entre duas strings. 0 significa que não há diferença entre duas strings, ou seja, que duas strings são iguais.As explicações intuitivas acima podem ajudar a lembrar a interpretação dos valores retornados, mas é ainda mais fácil verificar a documentação da biblioteca.
fonte
Você deve considerar que sistemas alternativos também podem ser decisões de design aceitáveis.
Blocos: 0 status de saída é verdadeiro, diferente de zero é falso
O exemplo de shells tratando um status de saída 0 como true já foi mencionado.
A lógica é que existe uma maneira de obter sucesso, mas há muitas maneiras de falhar; portanto, usar 0 como o valor especial que significa "sem erros" é pragmático.
Ruby: 0 é como qualquer outro número
Entre as linguagens de programação "normais", existem alguns outliers, como Ruby, que tratam 0 como um valor verdadeiro.
A lógica é que apenas
false
enil
deve ser falsa. Para muitos novatos em Ruby, é uma pegadinha. No entanto, em alguns casos, é bom que 0 seja tratado como qualquer outro número.No entanto, esse sistema funciona apenas em um idioma capaz de distinguir booleanos como um tipo separado de números. Nos primeiros dias da computação, os programadores que trabalhavam com linguagem assembly ou linguagem de máquina bruta não tinham tais luxos. Provavelmente, é natural tratar 0 como o estado "em branco" e definir um pouco como 1 como sinalizador quando o código detectar que algo aconteceu. Por extensão, a convenção desenvolveu que zero era tratado como falso e valores diferentes de zero passaram a ser tratados como verdadeiros. No entanto, não precisa ser assim.
Java: Os números não podem ser tratados como booleanos
Em Java,
true
efalse
são os únicos valores booleanos. Os números não são booleanos e nem podem ser convertidos em booleanos ( Java Language Specification, Seção 4.2.2 ):Essa regra evita a questão completamente - todas as expressões booleanas precisam ser explicitamente escritas no código.
fonte
if (thing === 0)
, isso não é legal.0
estejatrue
em uma linguagem dinâmica. Às vezes, eu pegava um0
ao tentar pegarNone
no Python, e isso às vezes pode ser bem difícil de detectar.if [ 0 ] ; then echo this executes ; fi
. O valor de dados falsos é uma cadeia vazia e uma falsidade testável é um status de finalização com falha de um comando, representado por um valor diferente de zero.Antes de abordar o caso geral, podemos discutir seus exemplos contrários.
Comparações de strings
O mesmo vale para muitos tipos de comparações, na verdade. Tais comparações calculam uma distância entre dois objetos. Quando os objetos são iguais, a distância é mínima. Portanto, quando a "comparação é bem-sucedida", o valor é 0. Mas, na verdade, o valor de retorno de não
strcmp
é um booleano, é uma distância, e é isso que as armadilhas ignoram os programadores .if (strcmp(...)) do_when_equal() else do_when_not_equal()
Em C ++, podemos redesenhar
strcmp
para retornar umDistance
objeto, que substituioperator bool()
para retornar true quando 0 (mas você seria mordido por um conjunto diferente de problemas). Ou, na linguagem C, basta ter umastreq
função que retorne 1 quando as strings forem iguais e 0, caso contrário.Chamadas API / código de saída do programa
Aqui você se preocupa com o motivo pelo qual algo deu errado, porque isso levará as decisões a erro. Quando as coisas dão certo, você não quer saber nada em particular - sua intenção é realizada. O valor de retorno deve, portanto, transmitir essas informações. É não um booleano, é um código de erro. O valor do erro especial 0 significa "sem erro". O restante do intervalo representa erros significativos localmente com os quais você precisa lidar (incluindo 1, que geralmente significa "erro não especificado").
Caso Geral
Isso nos deixa com a pergunta: por que os valores booleanos
True
eFalse
comumente representados com 1 e 0, respectivamente?Bem, além do argumento subjetivo "parece melhor assim", aqui estão algumas razões (subjetivas também) em que posso pensar:
analogia do circuito elétrico. A corrente está LIGADA por 1s e DESLIGADA por 0s. Eu gosto de ter (1, Sim, Verdadeiro, Ativado) juntos e (0, Não, Falso, Desativado), em vez de outro mix
inicializações de memória. Quando
memset(0)
um monte de variáveis (sejam elas ints, floats, bools), quero que seu valor corresponda às suposições mais conservadoras. Por exemplo, minha soma é inicialmente 0, o predicado é Falso etc.Talvez todas essas razões estejam ligadas à minha educação - se eu tivesse sido ensinado a associar 0 a True desde o início, eu faria o contrário.
fonte
bool
a ver.int
parabool
em muitas linguagens de programação. As coisas de comparação e gestos de erro são apenas exemplos de lugares em que a transmissão de outra maneira, além da que é feita atualmente, faria sentido.De uma perspectiva de alto nível, você está falando sobre três tipos de dados bastante diferentes:
Um booleano. A convenção matemática na álgebra booleana é usar 0 para
false
e 1 paratrue
, portanto, faz sentido seguir essa convenção. Eu acho que dessa maneira também faz mais sentido intuitivamente.O resultado da comparação. Isto tem três valores:
<
,=
e>
(aviso de que nenhum deles étrue
). Para eles, faz sentido usar os valores de -1, 0 e 1, respectivamente (ou, geralmente, um valor negativo, zero e um valor positivo).Se você deseja verificar a igualdade e você só tem uma função que realiza comparação geral, acho que você deve explicitar isso usando algo como
strcmp(str1, str2) == 0
. Acho que o uso!
nessa situação é confuso, porque trata um valor não-booleano como se fosse um booleano.Além disso, lembre-se de que comparação e igualdade não precisam ser a mesma coisa. Por exemplo, se você solicitar as pessoas pela data de nascimento, elas
Compare(me, myTwin)
devem retornar0
, masEquals(me, myTwin)
devem retornarfalse
.O sucesso ou falha de uma função, possivelmente também com detalhes sobre esse sucesso ou falha. Se você está falando do Windows, esse tipo é chamado
HRESULT
e um valor diferente de zero não indica necessariamente falha. De fato, um valor negativo indica falha e sucesso não negativo. O valor do sucesso é muito frequenteS_OK = 0
, mas também pode ser, por exemploS_FALSE = 1
, ou outros valores.A confusão vem do fato de que três tipos de dados logicamente bastante diferentes são realmente representados como um único tipo de dados (um número inteiro) em C e em alguns outros idiomas e que você pode usar números inteiros em uma condição. Mas acho que não faria sentido redefinir booleano para simplificar o uso de alguns tipos não-booleanos.
Além disso, considere outro tipo que é frequentemente usado em uma condição em C: um ponteiro. Lá, é natural tratar um
NULL
ponteiro (que é representado como0
) comofalse
. Portanto, seguir sua sugestão também tornaria o trabalho com ponteiros mais difícil. (Embora, pessoalmente, prefiro comparar explicitamente os ponteiros comNULL
, em vez de tratá-los como booleanos.)fonte
Zero pode ser falso, porque a maioria das CPUs possui um sinalizador ZERO que pode ser usado para ramificar. Ele salva uma operação de comparação.
Vamos ver o porquê.
Algum código psued, como o público provavelmente não lê assembly
o loop simples de origem c chama wibble 10 vezes
alguns fingem montagem para isso
c - fonte outro loop simples chama wibble 10 vezes
alguns fingem montagem para este caso
um pouco mais de fonte c
e a montagem
vê como é curto?
um pouco mais de fonte c
e o assembly (vamos assumir um compilador marginalmente inteligente que pode substituir == 0 sem comparação)
Agora vamos tentar uma convenção de true = 1
um pouco mais de fonte c #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()
e a montagem
ver quão curto é o caso com diferente de zero verdadeiro?
As CPUs muito antigas tinham pequenos conjuntos de sinalizadores anexados ao Acumulador.
Para verificar se a> b ou a = b geralmente usa uma instrução de comparação.
Vamos reafirmar isso. Em algumas CPUs mais antigas, você não precisava usar uma instrução de comparação para acumulador igual a ZERO ou acumulador menor que zero.
Agora você vê por que zero pode ser falso?
Observe que este é o código psuedo e nenhum conjunto de instruções real se parece com isso. Se você conhece montagem, sabe que estou simplificando bastante as coisas aqui. Se você conhece alguma coisa sobre o design do compilador, não precisa ler esta resposta. Qualquer pessoa que saiba algo sobre desenrolamento de loop ou previsão de ramificação, a classe avançada fica no final do corredor na sala 203.
fonte
if (foo)
eif (foo != 0)
deve gerar o mesmo código, e em segundo lugar, você está mostrando que a linguagem assembly que você está usando na verdade tem operandos booleanos explícitas e testes para eles. Por exemplo,jz
significajump if zero
. Em outras palavrasif (a == 0) goto target;
. E a quantidade nem está sendo testada diretamente; a condição é convertida em um sinalizador booleano que é armazenado em uma palavra de máquina especial. Na verdade, é mais parecido comcpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
jz
instrução, mas nãojnz
? (ou qualquer outro conjunto de instruções assimétrica condicionais)Há muitas respostas que sugerem que a correspondência entre 1 e verdadeira é necessária por alguma propriedade matemática. Não encontro nenhuma dessas propriedades e sugiro que seja uma convenção puramente histórica.
Dado um campo com dois elementos, temos duas operações: adição e multiplicação. Podemos mapear operações booleanas nesse campo de duas maneiras:
Tradicionalmente, identificamos True com 1 e False com 0. Identificamos AND com * e XOR com +. Assim OU é adição saturante.
No entanto, podemos facilmente identificar True com 0 e False com 1. Em seguida, identificamos OR com * e XNOR com +. Assim, AND é adição saturante.
fonte
Estranhamente, zero nem sempre é falso.
Em particular, a convenção Unix e Posix é definir
EXIT_SUCCESS
como 0 (eEXIT_FAILURE
como 1). Na verdade, é mesmo uma convenção C padrão !Portanto, para shells Posix e syscalls exit (2) , 0 significa "bem-sucedido", que intuitivamente é mais verdadeiro que falso.
Em particular, o shell
if
deseja que um retorno do processoEXIT_SUCCESS
(que seja 0) siga sua ramificação "then"!No Esquema (mas não no Common Lisp ou no MELT ) 0 e nulo (ou seja,
()
no Esquema) são verdadeiros, pois o único valor falso é#f
Eu concordo, eu estou nitpicking!
fonte
C é usado para programação de baixo nível perto do hardware, uma área na qual você às vezes precisa alternar entre operações lógicas e bit a bit, nos mesmos dados. Ser obrigado a converter uma expressão numérica em booleano apenas para executar um teste desorganizaria o código.
Você pode escrever coisas como:
ao invés de
Em um exemplo isolado, não é tão ruim, mas ter que fazer isso será cansativo.
Da mesma forma, converse operações. É útil para o resultado de uma operação booleana, como uma comparação, produzir apenas 0 ou 1: suponha que desejamos definir o terceiro bit de alguma palavra com base no fato de
modemctrl
a operadora detectar o bit:Aqui temos que ter
!= 0
, para reduzir o resultado da&
expressão biwise para0
ou1
, mas como o resultado é apenas um número inteiro, não somos obrigados a adicionar uma conversão irritante para converter booleano em número inteiro.Embora o C moderno agora tenha um
bool
tipo, ele ainda preserva a validade do código como este, tanto por ser uma coisa boa quanto por causa da quebra maciça de compatibilidade com versões anteriores que seria causada de outra forma.Outro exemplo em que C é liso: testando duas condições booleanas como uma chave de quatro direções:
Você não poderia tirar isso do programador C sem lutar!
Por fim, C às vezes serve como um tipo de linguagem assembly de alto nível. Nas linguagens assembly, também não temos tipos booleanos. Um valor booleano é apenas um bit ou zero versus um valor diferente de zero em um local ou registro de memória. Um zero inteiro, zero booleano e o endereço zero são todos testados da mesma maneira nos conjuntos de instruções em linguagem assembly (e talvez até no ponto flutuante zero). A semelhança entre C e a linguagem assembly é útil, por exemplo, quando C é usado como o idioma de destino para compilar outro idioma (mesmo um que tenha fortemente digitado booleanos!)
fonte
Um valor booleano ou verdade possui apenas 2 valores. Verdadeiro e falso.
Estes não devem ser representados como números inteiros, mas como bits (0 e 1).
Dizer que qualquer outro número inteiro ao lado de 0 ou 1 não é falso é uma afirmação confusa. As tabelas de verdade lidam com valores de verdade, não com números inteiros.
De um valor de verdade prospectivo, -1 ou 2 quebrariam todas as tabelas de verdade e qualquer lógica booleana associada a elas.
A maioria dos idiomas geralmente possui um
boolean
tipo que, quando convertido para um tipo de número, como número inteiro, revela que falso é convertido como um valor inteiro 0.fonte
TRUE
ouFALSE
. Nunca disse - talvez sim, mas não foi intencional - números inteiros eram verdadeiros ou falsos, perguntei por que eles avaliam o que quer que seja quando convertidos em booleanos.Por fim, você está falando em quebrar a linguagem principal porque algumas APIs são ruins. APIs ruins não são novas e você não pode corrigi-las quebrando o idioma. É um fato matemático que 0 é falso e 1 é verdadeiro, e qualquer linguagem que não respeite isso é fundamentalmente interrompida. A comparação de três vias é um nicho e não tem nenhum negócio, tendo o resultado convertido implicitamente em,
bool
pois retorna três resultados possíveis. As APIs antigas do C simplesmente têm um tratamento de erros terrível e também são prejudicadas porque o C não possui os recursos de linguagem necessários para não ter interfaces terríveis.Observe que não estou dizendo isso para idiomas que não possuem conversão implícita de número inteiro-> booleano.
fonte
+
símbolo denota OR. Então, por exemplo,abc + a'b'c
significa(a and b and c) or (a and (not b) and (not c))
.