Dlaczego 0 jest fałszem?

115

To pytanie może wydawać się głupie, ale dlaczego 0ewaluuje falsei jakąkolwiek inną wartość [całkowitą] trueto większość języków programowania?

Porównanie ciągów

Ponieważ pytanie wydaje się nieco zbyt proste, wyjaśnię się trochę bardziej: po pierwsze, może się wydawać oczywiste dla każdego programisty, ale dlaczego nie byłoby języka programowania - może być, ale nie może Użyłem - gdzie 0ewaluuje truei wszystkie inne wartości [całkowite] false? Ta jedna uwaga może wydawać się przypadkowa, ale mam kilka przykładów, w których może to być dobry pomysł. Po pierwsze, weźmy przykład trójstronnego porównania ciągów, wezmę C strcmpjako przykład: każdy programista próbujący C jako swojego pierwszego języka może ulec pokusie napisania następującego kodu:

if (strcmp(str1, str2)) { // Do something... }

Ponieważ strcmpzwroty, 0które oceniają, falsekiedy łańcuchy są równe, to, co początkowy programista próbował zrobić, kończy się niepowodzeniem i na ogół nie rozumie dlaczego na początku. Oceniając, 0że truezamiast tego, funkcja ta mogłaby zostać użyta w jej najprostszym wyrażeniu - powyższym - przy porównywaniu pod kątem równości, a odpowiednie kontrole dla -1i 1zostałyby wykonane tylko w razie potrzeby. Przez większość czasu uważalibyśmy ten typ zwrotu za bool(naszym zdaniem ).

Ponadto, niech wprowadzi nowy typ, sign, że po prostu ma wartości -1, 0a 1. To może być całkiem przydatne. Wyobraź sobie, że w C ++ jest operator statku kosmicznego i my go potrzebujemy std::string(cóż, jest już comparefunkcja, ale operator statku kosmicznego jest bardziej zabawny). Deklaracja będzie obecnie następująca:

sign operator<=>(const std::string& lhs, const std::string& rhs);

Gdybyśmy 0zostali oceniani true, operator statku kosmicznego nawet by nie istniał i moglibyśmy w operator==ten sposób zadeklarować :

sign operator==(const std::string& lhs, const std::string& rhs);

To operator==poradziłoby sobie z porównywaniem w trzech kierunkach jednocześnie i nadal mogłoby być użyte do wykonania następującego sprawdzenia, a jednocześnie byłoby w stanie sprawdzić, który ciąg znaków jest lepszy leksykograficznie od drugiego w razie potrzeby:

if (str1 == str2) { // Do something... }

Stara obsługa błędów

Mamy teraz wyjątki, więc ta część dotyczy tylko starych języków, w których nic takiego nie istnieje (na przykład C). Jeśli spojrzymy na standardową bibliotekę C (a także POSIX), możemy zobaczyć na pewno, że funkcje maaaaany powrócą 0po pomyślnym zakończeniu, a dowolna liczba całkowita w przeciwnym razie. Niestety niektórzy ludzie robią takie rzeczy:

#define TRUE 0
// ...
if (some_function() == TRUE)
{
    // Here, TRUE would mean success...
    // Do something
}

Jeśli myślimy o tym, jak myślimy w programowaniu, często mamy następujący wzór rozumowania:

Do something
Did it work?
Yes ->
    That's ok, one case to handle
No ->
    Why? Many cases to handle

Jeśli pomyślimy o tym jeszcze raz, sensowne byłoby umieszczenie jedynej wartości neutralnej 0, do yes(i tak działają funkcje C), podczas gdy wszystkie inne wartości mogą tam być, aby rozwiązać wiele przypadków no. Jednak we wszystkich językach programowania, które znam (oprócz być może niektórych eksperymentalnych języków ezoterycznych), które yesoceniają falsepod ifwarunkiem, podczas gdy wszystkie noprzypadki oceniają true. Istnieje wiele sytuacji, w których „to działa” reprezentuje jeden przypadek, podczas gdy „to nie działa” reprezentuje wiele prawdopodobnych przyczyn. Jeśli pomyślimy o tym w ten sposób, dokonanie 0oceny truei reszty do falsebyłoby znacznie bardziej sensowne.

Wniosek

Mój wniosek jest zasadniczo moim pierwotnym pytaniem: dlaczego zaprojektowaliśmy języki, gdzie 0falsei inne wartości true, biorąc pod uwagę moje kilka przykładów powyżej, a może kilka innych, o których nie myślałem?

Dalsze działania: Miło jest widzieć, że istnieje wiele odpowiedzi z wieloma pomysłami i tak wiele możliwych powodów, aby tak było. Uwielbiam, jak bardzo pasjonujesz się tym. Oryginalnie zadałem to pytanie z nudów, ale ponieważ wydajesz się tak namiętny, postanowiłem pójść nieco dalej i zapytać o uzasadnienie logicznego wyboru dla 0 i 1 na Math.SE :)

Morwenn
źródło
32
strcmp()nie jest dobrym przykładem wartości prawda lub fałsz, ponieważ zwraca 3 różne wartości. Będziesz zaskoczony, gdy zaczniesz używać powłoki, gdzie 0 oznacza prawda, a wszystko inne oznacza fałsz.
ott--
52
@ ott--: W powłokach uniksowych 0 oznacza sukces, a niezerowy oznacza porażkę - nie całkiem to samo, co „prawda” i „fałsz”.
Keith Thompson
14
@KeithThompson: W Bash (i innych powłokach) „sukces” i „porażka” naprawdę są takie same jak „prawda” i „fałsz”. Rozważmy na przykład instrukcję if true ; then ... ; fi, gdzie truejest poleceniem, które zwraca zero, a to każe ifuruchomić ....
ruakh
13
W sprzęcie nie ma żadnych boolanów, tylko liczby binarne, a w większości historycznych ISA liczba niezerowa jest uważana za „prawdziwą” we wszystkich instrukcjach warunkowego rozgałęziania (chyba że zamiast tego używają flag). Tak więc języki niskiego poziomu są bezwzględnie zobowiązane do przestrzegania podstawowych właściwości sprzętowych.
SK-logic
2
@MasonWheeler Posiadanie typu logicznego nic nie oznacza. Na przykład pyton nie ma booltypu, ale porównań / jeśli warunki itd. Może mieć dowolną wartość.
Bakuriu

Odpowiedzi:

98

0to falsedlatego, że oba są zerowymi elementami we wspólnych semirings . Mimo że są to odrębne typy danych, intuicyjny sens ma konwersja między nimi, ponieważ należą one do izomorficznych struktur algebraicznych.

  • 0to tożsamość dodawania i zero dla mnożenia. Dotyczy to liczb całkowitych i wymiernych, ale nie liczb zmiennoprzecinkowych IEEE-754: 0.0 * NaN = NaNi 0.0 * Infinity = NaN.

  • falseto tożsamość logiczna xor (⊻) i zero dla logicznej i (∧). Jeśli booleany są reprezentowane jako {0, 1} - zbiór liczb całkowitych modulo 2 - możesz myśleć o ⊻ jako dodatku bez przenoszenia i ∧ jako pomnożeniu.

  • ""i []są tożsamością do konkatenacji, ale istnieje kilka operacji, dla których mają sens jako zero. Powtarzanie jest jedno, ale powtarzanie i konkatenacja nie rozdzielają, więc te operacje nie tworzą semiery.

Takie niejawne konwersje są pomocne w małych programach, ale w dużych mogą utrudniać programowanie. To tylko jeden z wielu kompromisów w projektowaniu języka.

Jon Purdy
źródło
1
Miło, że wspomniałeś o listach. (BTW, nilczy zarówno pusta lista, jak []i falsewartość w Common Lisp; czy istnieje tendencja do łączenia tożsamości z różnych typów danych?) Nadal musisz wyjaśnić, dlaczego naturalne jest uznawanie fałszu za tożsamość addytywną i prawdziwego za tożsamość multiplikatywną i nie na odwrót. Czy nie można uznać trueza identyfikator ANDi zero dla OR?
Giorgio
3
+1 za odniesienie do podobnych tożsamości. Wreszcie odpowiedź, która nie sprowadza się tylko do „konwencji, radzenia sobie z nią”.
l0b0
5
+1 za podanie szczegółów konkretnej i bardzo starej matematyki, w której to przestrzegano i od dawna ma sens
Jimmy Hoffa
1
Ta odpowiedź nie ma sensu. trueto także tożsamość i zero semirings (boolean i / lub). Nie ma powodu, dla którego konwencja Appart jest do rozważenia falsebliższa 0 niż true.
TonioElGringo
1
@TonioElGringo: Różnica między prawdą a fałszem to różnica między XOR a XNOR. Można tworzyć pierścienie izomorficzne za pomocą AND / XOR, gdzie prawda to tożsamość multiplikatywna, a fałsz addytywny, lub z OR i XNOR, gdzie false to tożsamość multiplikatywna, a prawda to addytywny, ale XNOR zwykle nie jest uważany za wspólny podstawowa operacja taka jak XOR.
supercat,
74

Ponieważ matematyka działa.

FALSE OR TRUE is TRUE, because 0 | 1 is 1.

... insert many other examples here.

Tradycyjnie programy C mają takie warunki

if (someFunctionReturningANumber())

zamiast

if (someFunctionReturningANumber() != 0)

ponieważ pojęcie zerowego równoważnika fałszu jest dobrze zrozumiane.

Robert Harvey
źródło
21
Języki są tak zaprojektowane, ponieważ matematyka ma sens. To było pierwsze.
Robert Harvey
26
@Morwenn, to sięga 19 wieku i George Boole. Ludzie reprezentują False jako 0 i True jako! 0 dłużej niż były komputery.
Charles E. Grant
11
Nie rozumiem, dlaczego matematyka nie działa w drugą stronę, jeśli zmienisz tylko wszystkie definicje, tak aby AND to +, a OR to *.
Neil G,
7
Dokładnie: matematyka działa w obie strony, a odpowiedź na to pytanie wydaje się, że jest czysto konwencjonalna.
Neil G
6
@Robert Byłoby wspaniale, gdybyś mógł przeliterować „matematyczne podstawy” w swoim poście.
phant0m
38

Jak powiedzieli inni, matematyka była najważniejsza. Dlatego 0 to falsei 1 to true.

O której matematyce mówimy? Algebry boolowskie, które pochodzą z połowy XIX wieku, na długo przed pojawieniem się komputerów cyfrowych.

Można również powiedzieć, że konwencja wyszła z logiki zdań , która jest nawet starsza niż algebry boolowskie. Jest to sformalizowanie wielu logicznych wyników, które programiści znają i kochają ( false || xrówna się x, true && xrówna się xitd.).

Zasadniczo mówimy o arytmetyki na zbiorze z dwoma elementami. Pomyśl o liczeniu binarnym. Algebry boolowskie są źródłem tej koncepcji i jej teoretycznych podstaw. Konwencje języków takich jak C to tylko prosta aplikacja.

joshin4colours
źródło
2
Na pewno możesz. Ale utrzymanie tego „standardowego” sposobu dobrze pasuje do ogólnej arytmetyki (0 + 1 = 1, a nie 0 + 1 = 0).
joshin4colours
2
Tak, ale przypuszczalnie napisałbyś AND z + i OR z *, jeśli również odwrócisz definicje.
Neil G,
3
Matematyka nie była pierwsza. Matematyka rozpoznała, że ​​0 i 1 tworzą pole, w którym AND jest jak mnożenie, a OR jest jak dodawanie.
Kaz
1
@Kaz: Ale {0, 1} z OR i AND nie tworzy pola.
Giorgio
2
Trochę mnie to martwi, że więcej odpowiedzi i komentarzy to mówi true = 1. To nie jest całkiem dokładne, ponieważ true != 0to nie jest dokładnie to samo. Jednym z powodów (nie jedynym), dlaczego należy unikać takich porównań if(something == true) { ... }.
JensG,
26

Myślałem, że ma to związek z „dziedziczeniem” po elektronice, a także algebrą boolowską, gdzie

  • 0= off, negative, no,false
  • 1= on, positive, yes,true

strcmp zwraca 0, gdy łańcuchy są równe, ma związek z jego implementacją, ponieważ tak naprawdę robi to, aby obliczyć „odległość” między dwoma łańcuchami. To, że 0 również jest uważane za fałszywe, jest tylko zbiegiem okoliczności.

zwracanie 0 po sukcesie ma sens, ponieważ 0 w tym przypadku oznacza brak błędu i każda inna liczba byłaby kodem błędu. Użycie dowolnego innego numeru do sukcesu nie miałoby większego sensu, ponieważ masz tylko jeden kod sukcesu, a możesz mieć kilka kodów błędów. Używasz „Czy to zadziałało?” jako wyrażenie instrukcji if i powiedzenie 0 = yes miałoby większy sens, ale wyrażenie jest bardziej poprawne „Czy coś poszło nie tak?” i wtedy widać, że 0 = nie ma większego sensu. Myślenie o false/truetym naprawdę nie ma sensu, bo tak naprawdę jest no error code/error code.

Svish
źródło
Haha, jako pierwszy wyraźnie podałeś pytanie o błąd zwrotu. Wiedziałem już, że interpretuję to na swój własny sposób i można to zrobić w inny sposób, ale ty jako pierwszy wyraźnie to wyrażasz (spośród wielu odpowiedzi i komentarzy). Właściwie nie powiedziałbym, że ten czy inny sposób nie ma sensu, ale bardziej, że oba mają sens na różne sposoby :)
Morwenn
1
Właściwie powiedziałbym, 0za success/no errorto jedyną rzeczą, która ma sens, kiedy inne całkowite reprezentują kody błędów. To 0również zdarza się reprezentować falsew innych przypadkach, nie ma to tak naprawdę znaczenia, ponieważ nie mówimy tutaj wcale o prawdzie lub fałszu;)
Svish
Miałem ten sam pomysł, więc
poprawiłem
1
Twój punkt na temat strcmp()obliczania odległości jest całkiem dobry. Gdyby to zostało nazwane strdiff()następnie if (!strdiff())byłoby bardzo logiczne.
Kevin Cox,
„elektronika [...] gdzie 0 = [...] fałsz, 1 = [...] prawda” - nawet w elektronice jest to tylko konwencja i nie jest jedyna. Nazywamy to logiką dodatnią, ale można również użyć logiki ujemnej, gdzie dodatnie napięcie oznacza fałsz, a ujemne oznacza prawdę. Następnie obwód, którego użyjesz dla AND, staje się OR, OR staje AND, i tak dalej. Zgodnie z prawem De Morgana wszystko to jest równoważne. Czasami można znaleźć część obwodu elektronicznego zaimplementowaną dla wygody w logice ujemnej, w którym to momencie nazwy sygnałów w tej części są oznaczone kreską nad nimi.
Jules
18

Jak wyjaśniono w tym artykule , wartości falsei truenie należy ich mylić z liczbami całkowitymi 0 i 1, ale można je utożsamiać z elementami pola Galois (pole skończone) dwóch elementów (patrz tutaj ).

Pole to zbiór dwóch operacji, które spełniają określone aksjomaty.

Symbole 0 i 1 są konwencjonalnie używane do oznaczenia addytywnej i multiplikatywnej tożsamości pola, ponieważ liczby rzeczywiste są również polem (ale nie skończonym), którego tożsamości to liczby 0 i 1.

Tożsamość addytywna to element 0 pola, taki że dla wszystkich x:

x + 0 = 0 + x = x

a tożsamość multiplikatywna to element 1 pola, taki że dla wszystkich x:

x * 1 = 1 * x = x

Pole skończone dwóch elementów ma tylko te dwa elementy, mianowicie addytywną tożsamość 0 (lub false) i multiplikatywną tożsamość 1 (lub true). Dwie operacje tego pola to logiczny XOR (+) i logiczny AND (*).

Uwaga. Jeśli odwrócisz operacje (XOR to mnożenie, a AND to dodawanie), to mnożenie nie rozdziela po dodaniu i nie masz już pola. W takim przypadku nie ma powodu, aby wywoływać dwa elementy 0 i 1 (w dowolnej kolejności). Zauważ też, że nie możesz wybrać operacji OR zamiast XOR: bez względu na to, jak interpretujesz OR / AND jako dodawanie / mnożenie, wynikowa struktura nie jest polem (nie wszystkie elementy odwrotne istnieją zgodnie z wymaganiami aksjomatów pola).

Odnośnie funkcji C:

  • Wiele funkcji zwraca liczbę całkowitą, która jest kodem błędu. 0 oznacza BRAK BŁĘDU.
  • Intuicyjnie funkcja strcmpoblicza różnicę między dwoma łańcuchami. 0 oznacza, że ​​nie ma różnicy między dwoma ciągami, tzn. Że dwa ciągi są równe.

Powyższe intuicyjne wyjaśnienia mogą pomóc zapamiętać interpretację zwracanych wartości, ale jeszcze łatwiej jest po prostu sprawdzić dokumentację biblioteki.

Giorgio
źródło
1
+1 za pokazanie, że jeśli dowolnie je podmienisz, matematyka nie będzie już działać.
Jimmy Hoffa
2
Odwrócone: Biorąc pod uwagę pole z dwoma elementami i operacjami * i +, identyfikujemy True za pomocą 0, a False za pomocą 1. Identyfikujemy OR za pomocą *, a XOR za pomocą +.
Neil G
1
Przekonasz się, że obie te identyfikacje są wykonywane w tym samym polu i oba są zgodne z regułami logiki boolowskiej. Twoja notatka jest niestety niepoprawna :)
Neil G
1
Jeśli założymy, że True = 0, a XOR to +, to True musi być tożsamością dla XOR. Ale to nie dlatego, że prawda XOR prawda = fałsz. Chyba że ponownie zdefiniujesz operację XOR na True, aby True XOR True = True. Wtedy oczywiście twoja konstrukcja działa, ponieważ właśnie zmieniłeś nazwę rzeczy (w dowolnej strukturze matematycznej zawsze możesz z powodzeniem dokonać permutacji nazwy i uzyskać strukturę izomorficzną). Z drugiej strony, jeśli pozwolisz, aby prawda, fałsz i XOR miały swoje zwykłe znaczenie, wówczas prawda XOR prawda = fałsz i prawda nie mogą być addytywną tożsamością, tzn. Prawda nie może być zerowa
Giorgio
1
@Giorgio: Poprawiłem konstrukcję pod kątem komentarza w moim ostatnim komentarzu…
Neil G
13

Należy wziąć pod uwagę, że alternatywne systemy mogą być również akceptowalnymi decyzjami projektowymi.

Powłoki: 0 stan wyjścia jest prawdą, niezerowe to fałsz

Podano już przykład powłok traktujących status wyjścia 0 jako prawdziwy.

$ ( exit 0 ) && echo "0 is true" || echo "0 is false"
0 is true
$ ( exit 1 ) && echo "1 is true" || echo "1 is false"
1 is false

Uzasadnieniem jest to, że istnieje jeden sposób na sukces, ale wiele sposobów na porażkę, więc użycie 0 jako specjalnej wartości oznaczającej „brak błędów” jest pragmatyczne.

Ruby: 0 jest jak każda inna liczba

Wśród „normalnych” języków programowania istnieją pewne wartości odstające, takie jak Ruby, które traktują 0 jako prawdziwą wartość.

$ irb
irb(main):001:0> 0 ? '0 is true' : '0 is false'
=> "0 is true"

Uzasadnieniem jest to, że tylko falsei nilpowinny być fałszywe. Dla wielu nowicjuszy Ruby jest to problem. Jednak w niektórych przypadkach dobrze, że 0 jest traktowane jak każda inna liczba.

irb(main):002:0> (pos = 'axe' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "Found x at position 1"
irb(main):003:0> (pos = 'xyz' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "Found x at position 0"
irb(main):004:0> (pos = 'abc' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "x not found"

Jednak taki system działa tylko w języku, który jest w stanie odróżnić booleany jako osobny typ od liczb. We wcześniejszych czasach komputerów programiści pracujący z językiem asemblera lub surowym językiem maszynowym nie mieli takich luksusów. Prawdopodobnie naturalne jest traktowanie 0 jako stanu „pustego” i ustawianie wartości 1 na flagę, gdy kod wykryje, że coś się wydarzyło. W konsekwencji konwencja opracowała, że ​​zero traktowano jako fałsz, a wartości niezerowe zaczęto traktować jako prawdziwe. Jednak nie musi tak być.

Java: Liczby w ogóle nie mogą być traktowane jako logiczne

W języku Java, truea falsesą tylko wartości logiczne. Liczby nie są liczbami logicznymi i nie można ich nawet przeliczyć na wartości logiczne ( Java Language Specification, Sec 4.2.2 ):

Nie ma rzutowań między typami integralnymi a typem boolean.

Ta reguła po prostu całkowicie eliminuje pytanie - wszystkie wyrażenia logiczne muszą być wyraźnie zapisane w kodzie.

200_sukces
źródło
1
Rebol i Red traktują INTEGER o wartości 0! wartości jako prawda i mają osobne BRAK! typ (z tylko jedną wartością, BRAK) traktowany jako warunek fałszywy oprócz LOGIC! fałszywe. Znalazłem znaczną frustrację podczas próby napisania kodu JavaScript, który traktuje 0 jako fałsz; jest to niezwykle niezdarna decyzja dla dynamicznie pisanego języka. Jeśli chcesz przetestować coś, co może mieć wartość zero lub 0, musisz napisać if (thing === 0), to po prostu nie jest fajne.
HostileFork
@HostileFork Nie wiem. Uważam, że to ma sens, że 0jest true(jak każdy inny Integer) w dynamicznym języku. Czasami zdarzyło mi się złapać, 0gdy próbuję złapać Nonew Pythonie, a czasem może to być dość trudne do wykrycia.
Morwenn,
2
Ruby nie jest wartością odstającą. Ruby bierze to od Lispa (Ruby jest nawet potajemnie nazywana „MatzLisp”). Lisp to główny język w informatyce. Zero jest właśnie prawdziwa wartość w powłoce POSIX, ponieważ jest to fragment tekstu: if [ 0 ] ; then echo this executes ; fi. Fałszywa wartość danych jest pusty ciąg znaków, a sprawdzalne fałsz to udało stan wypowiedzenie komendy, która jest reprezentowana przez nie -ZERO.
Kaz
8

Przed zajęciem się ogólną sprawą możemy omówić twoje przykłady dotyczące przeciwników.

Porównanie ciągów

To samo dotyczy wielu rodzajów porównań. Takie porównania obliczają odległość między dwoma obiektami. Gdy obiekty są równe, odległość jest minimalna. Kiedy więc „porównanie się powiedzie”, wartość wynosi 0. Ale tak naprawdę wartość zwracana niestrcmp jest wartością logiczną, jest to odległość i to, co pułapkuje nieświadomych programistów .if (strcmp(...)) do_when_equal() else do_when_not_equal()

W C ++ możemy przeprojektować, strcmpaby zwrócić Distanceobiekt, który zastępuje operator bool()zwracanie true, gdy 0 (ale wtedy ugryzłby cię inny zestaw problemów). Lub w zwykłym C mają po prostu streqfunkcję, która zwraca 1, gdy łańcuchy są równe, a 0 w przeciwnym razie.

Wywołania API / kod wyjścia programu

Tutaj zależy Ci na tym, dlaczego coś poszło nie tak, ponieważ spowoduje to błędne decyzje. Kiedy wszystko się powiedzie, nie chcesz wiedzieć nic konkretnego - twoje zamiary są realizowane. Zwracana wartość musi zatem przekazywać te informacje. To nie jest wartość logiczna, to kod błędu. Specjalna wartość błędu 0 oznacza „brak błędu”. Reszta zakresu reprezentuje lokalnie znaczące błędy, z którymi musisz sobie poradzić (w tym 1, co często oznacza „nieokreślony błąd”).

Ogólna sprawa

To pozostawia nas z pytaniem: dlaczego są wartości logiczne Truei Falsepowszechnie reprezentowane z 1 i 0, odpowiednio?

Poza subiektywnym argumentem „tak jest lepiej”, oto kilka powodów (również subiektywnych), o których mogę myśleć:

  • analogia obwodu elektrycznego. Prąd jest WŁĄCZONY przez 1 s, a WYŁĄCZONY przez 0 s. Lubię mieć (1, Tak, Prawda, Wł.) Razem i (0, Nie, Fałsz, Wył.) Zamiast innej mieszanki

  • inicjalizacje pamięci. Kiedy jestem memset(0)zbiorem zmiennych (niezależnie od tego, czy są to liczby całkowite, zmiennoprzecinkowe, boolowe), chcę, aby ich wartość odpowiadała najbardziej konserwatywnym założeniom. Np. Moja suma wynosi początkowo 0, predykat to Fałsz itp.

Być może wszystkie te powody są związane z moją edukacją - gdybym nauczył mnie kojarzyć 0 z True od samego początku, poszedłbym na odwrót.

użytkownik44761
źródło
2
W rzeczywistości istnieje co najmniej jeden język programowania, który traktuje 0 jako prawdziwe. Powłoka unixowa.
Jan Hudec
+1 za rozwiązanie prawdziwego problemu: większość pytań Morwenna w ogóle nie dotyczy bool.
dan04
@ dan04 Jest. Cały post jest o uzasadnienie wyboru obsady od intcelu boolw wielu językach programowania. Porównywanie i wykonywanie gestów błędów to tylko przykłady miejsc, w których rzutowanie go w inny sposób niż ten, który obecnie wykonuje, miałoby sens.
Morwenn
6

Z wysokiego poziomu mówisz o trzech zupełnie różnych typach danych:

  1. Wartość logiczna. Konwencja matematyczna w algebrze boolowskiej polega na użyciu 0 dla falsei 1 dla true, więc warto stosować się do tej konwencji. Myślę, że ten sposób ma również bardziej intuicyjny sens.

  2. Wynik porównania. To ma trzy wartości: <, =i >(Zauważ, że żaden z nich nie jest true). Dla nich sensowne jest użycie odpowiednio wartości -1, 0 i 1 (lub, bardziej ogólnie, wartości ujemnej, zero i wartości dodatniej).

    Jeśli chcesz sprawdzić równość i masz tylko funkcję, która wykonuje ogólne porównanie, myślę, że powinieneś to wyraźnie określić za pomocą czegoś podobnego strcmp(str1, str2) == 0. Używanie !w tej sytuacji jest mylące, ponieważ traktuje wartość niebędącą wartością logiczną tak, jakby była wartością logiczną.

    Pamiętaj też, że porównanie i równość nie muszą być tym samym. Na przykład, jeśli zamówisz ludzi według daty urodzenia, Compare(me, myTwin)powinien powrócić 0, ale Equals(me, myTwin)powinien powrócić false.

  3. Sukces lub niepowodzenie funkcji, być może również ze szczegółami na temat tego sukcesu lub niepowodzenia. Jeśli mówisz o systemie Windows, ten typ jest wywoływany, HRESULTa niezerowa wartość niekoniecznie oznacza awarię. W rzeczywistości wartość ujemna oznacza niepowodzenie i nieujemny sukces. Wartość sukcesu jest bardzo często S_OK = 0, ale może to być na przykład S_FALSE = 1inne wartości.

Zamieszanie wynika z faktu, że trzy logicznie całkiem różne typy danych są w rzeczywistości reprezentowane jako pojedynczy typ danych (liczba całkowita) w C i niektórych innych językach oraz że można użyć liczby całkowitej w jednym warunku. Ale nie sądzę, aby sensowne było redefiniowanie wartości logicznej, aby uprościć korzystanie z niektórych typów nie-logicznych.

Weź również pod uwagę inny typ, który jest często używany w warunku w C: wskaźnik. Tam naturalne jest traktowanie jako NULL-pointer (który jest reprezentowany jako 0) jako false. Zatem zastosowanie się do Twojej sugestii utrudniłoby również pracę ze wskaźnikami. (Chociaż osobiście wolę jawnie porównywać wskaźniki NULL, niż traktować je jako logiczne).

svick
źródło
4

Zero może być fałszywe, ponieważ większość procesorów ma flagę ZERO, której można użyć do rozgałęzienia. Zapisuje operację porównania.

Zobaczmy dlaczego.

Niektóre psuedocode, ponieważ publiczność prawdopodobnie nie czyta asemblera

c- źródło prostych wywołań pętli wibble 10 razy

for (int foo =10; foo>0; foo-- ) /* down count loop is shorter */
{  
   wibble();
}

trochę udawać, że to zgromadziło

0x1000 ld a 0x0a      'foo=10
0x1002 call 0x1234    'call wibble()
0x1005 dec a          'foo--
0x1006 jrnz -0x06      'jump back to 0x1000 if not zero
0x1008  

c- źródło kolejnej prostej wywołania pętli wibble 10 razy

for (int foo =0; foo<10; foo-- ) /* up count loop is longer  */
{  
   wibble();
}

trochę udawaj, że to sprawa

0x1000 ld a 0x00      'foo=0
0x1002 call 0x1234    'call wibble()
0x1005 dec a          'foo--
0x1006 cmp 0x0a       'compare foo to 10 ( like a subtract but we throw the result away)
0x1008 jrns -0x08      'jump back to 0x1000 if compare was negative
0x100a  

trochę więcej c źródła

int foo=10;
if ( foo ) wibble()

i zgromadzenie

0x1000 ld a 0x10
0x1002 jz 0x3
0x1004 call 0x1234
0x1007  

widzisz jak to jest krótkie?

trochę więcej c źródła

int foo=10;
if ( foo==0 ) wibble()

i zespół (załóżmy, że marginalnie inteligentny kompilator może zastąpić == 0 bez porównania)

0x1000 ld a 0x10
0x1002 jz 0x3
0x1004 call 0x1234
0x1007  

Teraz wypróbujmy konwencję true = 1

trochę więcej c źródło # zdefiniować PRAWDA 1 int foo = PRAWDA; if (foo == TRUE) wibble ()

i zgromadzenie

0x1000 ld a 0x1
0x1002 cmp a 0x01
0x1004 jz 0x3
0x1006 call 0x1234
0x1009 

widzisz, jak krótki jest przypadek z niezerową wartością true?

Naprawdę wczesne procesory miały małe zestawy flag przymocowane do akumulatora.

Aby sprawdzić, czy a> b lub a = b ogólnie wymaga instrukcji porównania.

  • Chyba że B jest albo ZERO - w takim przypadku flaga ZERO jest ustawiona Zaimplementowany jako prosty logiczny NOR lub wszystkie bity w akumulatorze.
  • Lub NEGATYWNY, w którym wystarczy użyć „bitu znakowego”, tj. Najbardziej znaczącego bitu Akumulatora, jeśli używasz arytmetyki dopełniacza dwóch. (Głównie tak)

Przeanalizujmy to. Na niektórych starszych procesorach nie trzeba było używać instrukcji porównywania dla akumulatora równego ZERO lub akumulatora mniejszego niż zero.

Czy rozumiesz teraz, dlaczego zero może być fałszywe?

Uwaga: jest to kod psuedo i żaden zestaw instrukcji nie wygląda tak. Jeśli znasz się na montażu, wiesz, że tutaj dużo upraszczam. Jeśli wiesz coś o projektowaniu kompilatora, nie musiałeś czytać tej odpowiedzi. Każdy, kto wie coś na temat rozwijania pętli lub przewidywania gałęzi, klasa zaawansowana jest w korytarzu w pokoju 203.

Tim Williscroft
źródło
2
Punkt nie jest dobrze zrobiony tutaj, ponieważ na jednej rzeczy if (foo)i if (foo != 0)powinien generować ten sam kod, a po drugie, jesteś pokazując, że język asemblera używasz w rzeczywistości ma jednoznacznych argumentów logicznych i testów dla nich. Na przykład jzoznacza jump if zero. Innymi słowy if (a == 0) goto target;. A ilość nie jest nawet testowana bezpośrednio; warunek jest konwertowany na jego flagę logiczną, która jest przechowywana w specjalnym słowie maszynowym. Właściwie to bardziejcpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
Kaz
Nie, Kaz, starsze procesory tak nie działały. Jz / jnz można wykonać bez wykonywania instrukcji porównania. Co tak naprawdę stanowiło sens całego mojego postu.
Tim Williscroft,
2
Nie napisałem nic o instrukcji porównania.
Kaz
Czy możesz podać procesor, który ma jzinstrukcję, ale nie jnz? (lub jakikolwiek inny asymetryczny zestaw instrukcji warunkowych)
Toby Speight
3

Istnieje wiele odpowiedzi, które sugerują, że pewna właściwość matematyczna wymaga zgodności między 1 a prawdą. Nie mogę znaleźć żadnej takiej nieruchomości i sugeruję, że jest to konwencja czysto historyczna.

Biorąc pod uwagę pole z dwoma elementami, mamy dwie operacje: dodawanie i mnożenie. Możemy zmapować operacje logiczne na tym polu na dwa sposoby:

Tradycyjnie identyfikujemy Prawda z 1, a Fałsz z 0. Identyfikujemy ORAZ *, a XOR z +. Zatem OR jest nasycającym dodatkiem.

Jednak równie łatwo moglibyśmy zidentyfikować Prawda z 0, a Fałsz z 1. Następnie identyfikujemy OR z * i XNOR z +. Zatem AND jest nasycającym dodatkiem.

Neil G.
źródło
4
Gdybyś podążał za linkiem na wikipedii, mógłbyś odkryć, że koncepcja algebry boolowskiej jest zamknięta, związana z pojęciem pola dwóch elementów Galois ( en.wikipedia.org/wiki/GF%282%29 ). Symbole 0 i 1 są konwencjonalnie używane do oznaczenia odpowiednio addytywności i multiplikatywności, ponieważ liczby rzeczywiste są również polem, którego tożsamościami są liczby 0 i 1.
Giorgio
1
@NeilG Myślę, że Giorgio próbuje powiedzieć, że to coś więcej niż tylko konwencja. 0 i 1 w algebrze boolowskiej są w zasadzie takie same jak 0 i 1 w GF (2), które zachowują się prawie tak samo jak 0 i 1 w liczbach rzeczywistych w odniesieniu do dodawania i mnożenia.
svick
1
@svick: Nie, ponieważ możesz po prostu zmienić nazwę dodawania mnożącego i nasycającego na OR lub AND, a następnie odwrócić etykiety, aby 0 było Prawdą, a 1 Fałszem. Giorgio mówi, że była to konwencja logiki boolowskiej, która została przyjęta jako konwencja informatyki.
Neil G
1
@Neil G: Nie, nie można odwracać + i * oraz 0 i 1, ponieważ pole wymaga dystrybucji mnożenia zamiast dodawania (patrz en.wikipedia.org/wiki/Field_%28mathematics%29 ), ale jeśli ustawisz +: = AND i *: = XOR, otrzymujesz T XOR (T AND F) = T XOR F = T, podczas gdy (T XOR T) AND (T XOR F) = F AND T = F. Dlatego odwracając operacje i tożsamości, nie mam już pola. Zatem IMO definiujące 0 i 1 jako tożsamość odpowiedniego pola wydaje się wychwytywać fałszywe i prawdziwe całkiem wiernie.
Giorgio
1
@giorgio: Zredagowałem odpowiedź, aby było jasne, co się dzieje.
Neil G
3

O dziwo zero nie zawsze jest fałszywe.

W szczególności konwencja Unix i Posix ma być zdefiniowana EXIT_SUCCESSjako 0 (i EXIT_FAILUREjako 1). W rzeczywistości jest to nawet standardowa konwencja C. !

Tak dla pocisków Posix i exit (2) wywołań systemowych 0 oznacza „sukces”, co intuicyjnie jest bardziej prawdziwe niż fałszywe.

W szczególności powłoka ifchce powrotu procesuEXIT_SUCCESS (czyli 0), aby podążał za swoją gałęzią „wtedy”!

Na schemacie (ale nie we wspólnym Lisp lub MELT ) 0 i zero (tj. Na ()schemacie) są prawdziwe, ponieważ jedyną fałszywą wartością jest#f

Zgadzam się, robię dupki!

Basile Starynkevitch
źródło
3

C jest używany do programowania niskopoziomowego blisko sprzętu, obszaru, w którym czasami trzeba przechodzić między operacjami bitowymi i logicznymi na tych samych danych. Konieczność konwersji wyrażenia liczbowego na wartość boolowską tylko w celu wykonania testu zaśmieciłaby kod.

Możesz pisać takie rzeczy jak:

if (modemctrl & MCTRL_CD) {
   /* carrier detect is on */
}

zamiast

if ((modemctrl & MCTRL_CD) != 0) {
    /* carrier detect is on */
}

W jednym odosobnionym przykładzie nie jest tak źle, ale zrobienie tego stanie się irytujące.

Podobnie, operacje konwersacyjne. Przydaje się w wyniku operacji boolowskiej, takiej jak porównanie, wygenerowanie 0 lub 1: Załóżmy, że chcemy ustawić trzeci bit jakiegoś słowa na podstawie tego, czy modemctrlbit wykrywa nośną:

flags |= ((modemctrl & MCTRL_CD) != 0) << 2;

Tutaj musimy mieć != 0, aby zredukować wynik &wyrażenia biwise do 0lub1 , ale ponieważ wynik jest tylko liczbą całkowitą, nie musimy dodawać denerwujących rzutów, aby dalej konwertować wartość logiczną na liczbę całkowitą.

Mimo że współczesne C ma teraz booltyp, nadal zachowuje ważność takiego kodu, zarówno dlatego, że jest to dobra rzecz, jak i z powodu ogromnego zerwania z kompatybilnością wsteczną, która byłaby spowodowana inaczej.

Kolejny przykład, w którym C jest zręczny: testowanie dwóch warunków boolowskich jako przełącznika czterokierunkowego:

switch (foo << 1 | bar) {  /* foo and bar booleans are 0 or 1 */
case 0: /* !foo && !bar */
   break;
case 1: /* !foo && bar */
   break;
case 2: /* foo && !bar */
   break;
case 3: /* foo && bar */
   break;
}

Nie można tego zabrać programatorowi C bez walki!

Wreszcie C czasami służy jako rodzaj języka asemblera na wysokim poziomie. W językach asemblera nie mamy również typów boolowskich. Wartość logiczna to tylko odrobina lub zero w stosunku do niezerowej wartości w lokalizacji pamięci lub rejestrze. Liczba całkowita zero, zero logiczne i adres zero są testowane w ten sam sposób w zestawach instrukcji języka asemblera (a być może nawet zmiennoprzecinkowe zero). Podobieństwo między C a językiem asemblera jest przydatne, na przykład, gdy C jest używany jako język docelowy do kompilacji innego języka (nawet takiego, który ma mocno wpisane wartości logiczne!)

Kaz
źródło
0

Wartość logiczna lub prawda ma tylko 2 wartości. Prawda i fałsz.

Te nie powinny być przedstawione w postaci liczb całkowitych, ale jako bity (0 do 1).

Mówienie, że jakakolwiek inna liczba całkowita oprócz 0 lub 1 nie jest fałszem, jest mylącym stwierdzeniem. Tabele prawdy dotyczą wartości prawdy, a nie liczb całkowitych.

Z perspektywy wartości prawdy -1 lub 2 złamałoby wszystkie tabele prawdy i związaną z nimi logikę boolowską.

  • 0 ORAZ -1 ==?!
  • 0 LUB 2 ==?!

Większość języków ma zazwyczaj booleantyp, który po rzutowaniu na typ liczbowy, taki jak liczba całkowita, ujawnia wartość false do rzutowania jako liczba całkowita 0.

Jon Raynor
źródło
1
0 ORAZ -1 == dowolna wartość logiczna, na którą je rzuciłeś. O to właśnie chodzi w moim pytaniu, po co je przesyłać do TRUElub FALSE. Nigdy nie powiedziałem - może tak zrobiłem, ale nie było to zamierzone - liczby całkowite były prawdziwe lub fałszywe, zapytałem o to, dlaczego oceniają je w zależności od tego, które wartości mają wartość boolowską.
Morwenn
-6

Ostatecznie mówisz o złamaniu podstawowego języka, ponieważ niektóre interfejsy API są kiepskie. Crappy APIs nie są nowe i nie można ich naprawić, łamiąc język. Jest matematycznym faktem, że 0 jest fałszem, a 1 jest prawdą, a każdy język, który tego nie szanuje, jest zasadniczo uszkodzony. Trójstronne porównanie jest niszowe i nie ma żadnego interesu, ponieważ jego wynik niejawnie przekształca się wbool ponieważ zwraca trzy możliwe wyniki. Stare interfejsy API C po prostu mają straszliwą obsługę błędów, a także są hamowane, ponieważ C nie ma niezbędnych funkcji językowych, aby nie mieć strasznych interfejsów.

Zauważ, że nie mówię tego w przypadku języków, które nie mają niejawnej konwersji liczb całkowitych-> boolowskich.

DeadMG
źródło
11
„Jest matematycznym faktem, że 0 to fałsz, a 1 to prawda” Erm.
R. Martinho Fernandes
11
Czy możesz przytoczyć odniesienie do swojego „matematycznego faktu, że 0 to fałsz, a 1 to prawda”? Twoja odpowiedź brzmi niebezpiecznie jak rant.
Dan Pichelman
14
To nie jest fakt matematyczny, ale była konwencją matematyczną od XIX wieku.
Charles E. Grant
1
Algebra boolowska jest reprezentowana przez skończone pole, w którym 0 i 1 są elementami tożsamości dla operacji przypominających dodawanie i mnożenie. Operacjami tymi są odpowiednio OR i AND. W rzeczywistości algebra boolowska jest napisana podobnie do zwykłej algebry, gdzie zestawienie oznacza ORAZ, a +symbol oznacza LUB. Na przykład abc + a'b'coznacza (a and b and c) or (a and (not b) and (not c)).
Kaz