Dlaczego 0 jest fałszywe?

To pytanie może brzmieć głupio, ale dlaczego 0 daje wynik false i każda inna [integer] wartość true to większość języków programowania?

Porównanie ciągów

Ponieważ pytanie wydaje się trochę zbyt proste, wytłumaczę sobie trochę więcej: po pierwsze, może się to wydawać oczywiste każdemu programiście, ale dlaczego nie byłoby języka programowania – może być, ale nie taki, którego używałem – gdzie 0 zwraca true, a wszystkie inne wartości [integer] – false? Ta jedna uwaga może wydawać się przypadkowe, ale mam kilka przykładów, w których mógł to być dobry pomysł. Najpierw weźmy przykład trójstronnego porównania łańcuchów, wezmę C „s strcmp jako przykład: każdy programista próbujący C jako swój pierwszy język może ulec pokusie napisania następującego kodu:

Ponieważ strcmp zwraca 0 co daje false, gdy łańcuchy są równe, to, co próbował zrobić początkujący programista, kończy się niepowodzeniem i na początku nie rozumie, dlaczego. Gdyby zamiast tego 0 oceniono jako true, ta funkcja mogłaby zostać użyta w jej najprostszym wyrażeniu – powyższym – przy porównywaniu pod kątem równości, a odpowiednie testy -1 i 1 zostałyby przeprowadzone tylko wtedy, gdy są potrzebne. Przez większość czasu rozważalibyśmy zwracany typ jako bool (w naszym umyśle mam na myśli).

Ponadto wprowadźmy nowy typ, sign, który przyjmuje tylko wartości -1, 0 i 1. To może być całkiem przydatne. Wyobraź sobie, że istnieje operator statku kosmicznego w C ++ i chcemy go dla std::string (no cóż, jest już funkcja compare, ale operator statku kosmicznego jest fajniejszy). Deklaracja wyglądałaby obecnie następująco:

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

Gdy 0 zostało ocenione jako true, operator statku kosmicznego nawet by nie istniał, a moglibyśmy zadeklarować operator== w ten sposób:

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

To operator== może poradziły sobie z porównaniem trójetapowym naraz i nadal mogą być użyte do wykonania następującego sprawdzenia, podczas gdy nadal można sprawdzić, który ciąg jest leksykograficznie lepszy od drugiego w razie potrzeby:

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

Obsługa starych błędów

Mamy teraz wyjątki, więc ta część dotyczy tylko starych języków, w których nie ma czegoś takiego istnieją (na przykład C). Jeśli spojrzymy na standardową bibliotekę C (i jedną z POSIX), zobaczymy na pewno, że wiele funkcji zwraca 0 po pomyślnym zakończeniu i dowolną liczbę całkowitą w przeciwnym razie. Niestety, widziałem kilka osób rób takie rzeczy:

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

Jeśli pomyślimy o tym, jak myślimy programując, często mamy następujący wzorzec 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 ponownie, sensowne byłoby umieszczenie jedynej neutralnej wartości, 0, w yes (i tak właśnie C „s działają), podczas gdy wszystkie inne wartości mogą służyć do rozwiązywania wielu przypadków no. Jednak we wszystkich językach programowania, które znam (może z wyjątkiem niektórych eksperymentalnych języków ezoterycznych), który yes daje wynik false w stanie if, wh ile wszystkie no przypadki są oceniane jako true. Jest 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, mając 0 wyliczyć na true, a resztę na false miałoby dużo więcej sensu.

Wniosek

Mój wniosek jest zasadniczo moim pierwotnym pytaniem: dlaczego zaprojektowaliśmy języki, w których 0 jest false, a pozostałe wartości to true, biorąc pod uwagę kilka moich przykładów powyżej i może więcej, o których nie pomyślałem?

Kontynuacja: Miło jest widzieć wiele odpowiedzi zawierających wiele pomysłów i tylu możliwych powodów żeby tak było. Uwielbiam to, jak bardzo pasjonujesz się tym.Pierwotnie zadałem to pytanie z nudów, ale ponieważ wydajesz się być takim pasjonatem, postanowiłem pójść trochę dalej i zapytać o uzasadnienie wyboru boolowskiego dla 0 i 1 w Math.SE 🙂

Komentarze

  • strcmp() nie jest dobrym przykładem dla prawdy lub false, ponieważ zwraca 3 różne wartości. Będziesz zaskoczony, gdy zaczniesz używać powłoki, gdzie 0 oznacza prawdę, a wszystko inne fałsz.
  • @ ott–: W powłokach Uniksa 0 oznacza sukces , a nie -zero oznacza awarię – to nie to samo co ” true ” i ” false „.
  • @KeithThompson: In Bash (i inne powłoki), ” sukces ” i ” niepowodzenie ” to to samo co ” true ” i ” false „. Rozważmy na przykład instrukcję if true ; then ... ; fi, gdzie true jest poleceniem, które zwraca zero i mówi if do uruchomienia ....
  • W sprzęcie nie ma żadnych wartości logicznych, tylko liczby binarne, aw większości historycznych ISA uważa się za liczbę niezerową jako ” true ” we wszystkich warunkowych instrukcjach rozgałęziania (chyba że ' ponownie używają flag zamiast). Tak więc języki niskiego poziomu są z całą pewnością zobowiązane do przestrzegania podstawowych właściwości sprzętu.
  • @MasonWheeler Posiadanie typu boolowskiego nie ' nie sugeruje niczego. Na przykład python czy ma typ bool, ale porównania / warunki warunkowe itp. Mogą mieć dowolną wartość zwracaną.

Odpowiedź

0 to false, ponieważ oba są zero wspólnych elementów półrocze . Mimo że są to różne typy danych, konwersja między nimi ma intuicyjny sens, ponieważ należą one do izomorficznych struktur algebraicznych.

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

  • false to tożsamość dla Boolean xor (and) i zero dla Boolean i (∧). Jeśli booleany są reprezentowane jako {0, 1} – zbiór liczb całkowitych modulo 2 – możesz myśleć o ⊻ jako o dodawaniu bez przenoszenia, a ∧ jako o mnożeniu.

  • "" i [] to tożsamość do konkatenacji, ale istnieje kilka operacji, dla których mają one sens jako zero. Powtórzenie to jedno, ale powtórzenie i konkatenacja nie są dystrybuowane, więc te operacje nie tworzą semiru.

Takie niejawne konwersje są pomocne w małych programach, ale w dużych może utrudnić rozumowanie programów. To tylko jeden z wielu kompromisów w projektowaniu języka.

Komentarze

  • Fajnie, że wspomniałeś o listach. (BTW, nil to zarówno pusta lista [], jak i false wartość w Common Lisp ; czy istnieje tendencja do łączenia tożsamości z różnych typów danych?) Nadal musisz wyjaśnić, dlaczego naturalne jest uważanie fałszu za tożsamość addytywną, a prawdziwość za tożsamość multiplikatywną, a nie na odwrót. Czy ' nie można traktować true jako identyfikatora dla AND i zero dla OR?
  • +1 za odwoływanie się do podobnych tożsamości. Na koniec odpowiedź, która nie ' nie sprowadza się do ” konwencji, radzi sobie z tym „.
  • +1 za podanie szczegółów konkretnej i bardzo starej matematyki, w której było to stosowane i od dawna miało sens.
  • Ta odpowiedź nie ' t ma sens. true to także tożsamość i zero półingów (wartość logiczna i / lub). Nie ma powodu, aby uważać, że false jest bliżej 0 niż true.
  • @TonioElGringo: Różnica między prawdą a fałszem to różnica między XOR i XNOR. Pierścienie izomorficzne można tworzyć za pomocą AND / XOR, gdzie prawda jest identycznością multiplikatywną, a fałsz addytywną, lub z OR i XNOR, gdzie fałsz jest identycznością multiplikatywną, a prawda jest addytywną, ale XNOR zwykle nie jest uważany za pospolity podstawowa operacja taka jak XOR.

Odpowiedź

Ponieważ matematyka działa.

FALSE OR TRUE is TRUE, because 0 | 1 is 1. ... insert many other examples here. 

Tradycyjnie programy w języku C mają warunki takie jak

 if (someFunctionReturningANumber())  

zamiast

 if (someFunctionReturningANumber() != 0)  

ponieważ pojęcie zera jako odpowiednika fałszu jest dobrze rozumiane.

Komentarze

  • Języki są zaprojektowane w ten sposób, ponieważ matematyka ma sens. To było pierwsze.
  • @Morwenn, to sięga XIX wieku i Georgea Boolea. Ludzie reprezentują Fałsz jako 0, a Prawdę jako! 0 dłużej niż były komputery.
  • Nie ' nie rozumiem, dlaczego matematyka nie ' nie działa w drugą stronę, jeśli po prostu zmienisz wszystkie definicje, tak aby AND to +, a OR to *.
  • Dokładnie: matematyka działa w obie strony, a odpowiedź na wydaje się, że to pytanie jest czysto konwencjonalne.
  • @Robert It ' d byłoby wspaniale, gdybyś mógł przeliterować ” matematyczne podstawy ” w Twoim poście.

Odpowiedź

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

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

Można również powiedzieć, że konwencja ta wywodzi się z logiki zdań , starszej nawet niż algebry boolowskie. To jest formalizacja wielu logicznych wyników, które programiści znają i kochają (false || x równa się x, true && x równa się x i tak dalej).

Zasadniczo mówimy o arytmetyce na zbiorze z dwoma elementami. Pomyśl o liczeniu binarnym. Algebry Boolea są początkiem tego pojęcia i jego teoretycznym fundamentem. Konwencje języków takich jak C to tylko prosta aplikacja.

Komentarze

  • Możesz Na pewno. Ale zachowanie tego ” standardowego ” sposobu dobrze pasuje do arytmetyki ogólnej (0 + 1 = 1, a nie 0 + 1 = 0).
  • Tak, ale prawdopodobnie napisałbyś AND z + i OR z *, gdybyś również odwrócił definicje.
  • Matematyka nie ' t pierwsze. Matematyka rozpoznała, że 0 i 1 tworzą pole, w którym AND jest jak mnożenie, a OR jest jak dodawanie.
  • @ Kaz: Ale {0, 1} z OR i AND nie tworzy pola.
  • Trochę mi przeszkadza, że więcej odpowiedzi i komentarzy mówi, że true = 1. To ' nie jest do końca dokładne, ponieważ true != 0 nie jest dokładnie tym samym. Jeden (nie jedyny) powód, dla którego należy unikać porównań typu if(something == true) { ... }.

Odpowiedź

Myślałem, że ma to związek z „dziedziczeniem” elektroniki, 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ż to, co faktycznie robi, to obliczenie „odległości” między dwoma łańcuchami. To, że 0 również jest uważane za fałszywe, to tylko zbieg okoliczności.

zwrócenie 0 w przypadku sukcesu ma sens ponieważ 0 w tym przypadku oznacza brak błędu , a każda inna liczba byłaby kodem błędu. Użycie dowolnej innej liczby do sukcesu miałoby mniej sensu, ponieważ masz tylko jeden kod sukcesu, podczas gdy możesz mieć kilka kodów błędów. Używasz „Czy to zadziałało?” jak wyrażenie wyrażenia if i powiedz 0 = tak miałoby więcej sensu, ale wyrażenie jest bardziej poprawne „Czy coś poszło nie tak?” a potem widzisz, że 0 = nie ma wiele sensu. Myślenie o false/true nie ma tu sensu, ponieważ tak naprawdę jest no error code/error code.

Komentarze

  • Haha, jesteś pierwszą osobą, która wyraźnie zadała pytanie o zwracany błąd. Wiedziałem już, że zinterpretowałem to na swój własny sposób i można by zapytać w inny sposób, ale ' jako pierwszy wyrazisz to wprost (spośród wielu odpowiedzi i komentarzy).Właściwie nie ' nie powiedziałbym, że jeden lub drugi sposób nie ma sensu, ale co więcej, oba mają sens na różne sposoby 🙂
  • Właściwie ' d powiedz 0 for success/no error to jedyna rzecz, która ma sens, gdy inne liczby całkowite reprezentują kody błędów . To 0 zdarza się również reprezentować false w innych przypadkach nie ' nie ma znaczenia, ponieważ my nie ' w ogóle nie mówimy tutaj o prawdzie lub fałszu;)
  • Miałem ten sam pomysł, więc poprawiłem
  • Twój punkt widzenia strcmp() obliczenie odległości jest całkiem dobre. Gdyby nazywało się strdiff(), to if (!strdiff()) byłoby bardzo logiczne.
  • ” elektronika […] gdzie 0 = […] fałsz, 1 = […] prawda ” – nawet w elektronice jest to tylko konwencja i nie jest jedyną '. Nazywamy to logiką dodatnią, ale można również użyć logiki ujemnej, w której 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 się AND i tak dalej. Zgodnie z prawem De Morgana ', wszystko jest równoważne. Czasami ' znajdujesz część obwodu elektronicznego zaimplementowaną dla wygody w logice ujemnej, w którym to momencie nazwy sygnałów w tej części są zaznaczone kreską nad nimi.

Odpowiedź

Jak wyjaśniono w tym artykule , wartości false i true nie należy mylić z liczbami całkowitymi 0 i 1, ale można je zidentyfikować za pomocą elementów 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 oznaczania addytywnych i multiplikatywnych tożsamości pola, ponieważ liczby rzeczywiste są również polem (ale nie skończonym), którego tożsamościami są liczby 0 i 1.

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

x + 0 = 0 + x = x 

i tożsamość multiplikatywna jest elementem 1 pola, tak że dla wszystkich x:

x * 1 = 1 * x = x 

Skończone pole dwóch elementów ma tylko te dwa elementy, a mianowicie tożsamość addytywna 0 (lub false) i tożsamość multiplikatywna 1 (lub true). Dwie operacje na tym polu to logiczne XOR (+) i logiczne AND (*).

Uwaga. Jeśli odwrócisz operacje (XOR to mnożenie, a AND to dodawanie), to mnożenie nie jest rozdzielne względem dodawania i nie masz już pola. W takim przypadku nie masz powodu, aby wywoływać dwa elementy 0 i 1 (w dowolnej kolejności). Zauważ również, ż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 aksjomatami pola).

Odnośnie funkcji C:

  • Wiele funkcji zwraca liczbę całkowitą będącą kodem błędu. 0 oznacza BRAK BŁĘDU.
  • Intuicyjnie funkcja strcmp oblicza różnicę między dwoma ciągami. 0 oznacza, że nie ma różnicy między dwoma ciągami, tj. Że dwa ciągi są równe.

Powyższe intuicyjne wyjaśnienia mogą pomóc w zapamiętaniu interpretacji zwracanych wartości, ale jeszcze łatwiej jest po prostu sprawdź dokumentację biblioteki.

Komentarze

  • +1, aby pokazać, że jeśli zamieniasz je arbitralnie, matematyka już nie działa.
  • Odwrócony: biorąc pod uwagę pole z dwoma elementami i operacjami * i +, utożsamiamy True z 0 i False z 1. Identyfikujemy OR z * i XOR z +.
  • Przekonasz się, że oba tych identyfikacji odbywa się na tym samym polu i obie są zgodne z regułami logiki Boolea. Twoja notatka jest niestety niepoprawna 🙂
  • Jeśli przyjmiesz, że True = 0, a XOR to +, to True musi być tożsamością dla XOR. Ale to nie dlatego, że True XOR True = False. Chyba że ponownie zdefiniujesz operację XOR na True, tak aby True XOR True = True. Wtedy oczywiście twoja konstrukcja działa, ponieważ właśnie zmieniłeś nazwy rzeczy (w dowolnej strukturze matematycznej zawsze możesz z powodzeniem dokonać permutacji nazwy i uzyskać strukturę izomorficzną). Z drugiej strony, jeśli pozwolisz, aby True, False i XOR miały swoje zwykłe znaczenie, to True XOR True = False i True nie może być tożsamością addytywną, tj. True nie może wynosić 0.
  • @Giorgio: Poprawiłem moją konstrukcję zgodnie z Twoim komentarzem w moim ostatnim komentarzu…

Odpowiedź

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

Powłoki: kod zakończenia 0 to prawda, wartość niezerowa to fałsz

Przykład powłok, które traktują 0 status wyjścia jako prawdziwy został już wspomniany.

 $ ( 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  

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

Ruby: 0 jest jak każda inna liczba

Wśród „normalnych” języków programowania jest kilka wartości odstających, takich jak Ruby, które traktują 0 jako wartość prawdziwą.

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

jest taki, że tylko false i nil powinny być fałszywe. Dla wielu nowicjuszy w Rubim jest to „gotowa”. Jednak w niektórych przypadkach dobrze jest, gdy 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ć wartości logiczne jako oddzielny typ od liczb. We wcześniejszych czasach informatyki programiści pracujący w asemblerze lub surowym języku maszynowym nie mieli takich luksusów. Prawdopodobnie naturalne jest traktowanie 0 jako stanu „pustego” i ustawianie bitu na 1 jako flagi, gdy kod wykryje, że coś się stało. W związku z tym powstała konwencja, że zero było traktowane jako fałsz, a wartości niezerowe zaczęto traktować jako prawdziwe. Jednak nie musi tak być.

Java: liczb nie można w ogóle traktować jako wartości logicznych

W Javie true i false to jedyne wartości logiczne. Liczby nie są wartościami logicznymi i nie można ich nawet rzutować na wartości logiczne ( Specyfikacja języka Java, sekcja 4.2.2 ):

Nie ma rzutów między typami całkowitymi a typem boolean .

Ta reguła po prostu unika tego pytania – wszystkie wyrażenia boolowskie muszą być wyraźnie zapisane w kodzie.

Komentarze

  • Rebol i Red traktują wartości INTEGER! o wartości 0 jako prawdziwe i mają osobny typ NONE! (z tylko jedną wartością, NONE) traktowane jako warunkowe fałsz oprócz LOGIC! false. ' napotkałem znaczną frustrację podczas próby napisania kodu JavaScript, który traktuje 0 jako fałsz; an incr niezręczna decyzja dotycząca języka z dynamicznym typowaniem. Jeśli chcesz przetestować coś, co może mieć wartość null lub 0, kończy się to koniecznością napisania if (thing === 0), to po prostu nie jest fajne.
  • @HostileFork Nie ' nie wiem. Uważam, że ma sens, aby 0 to true (jak każda inna liczba całkowita) w języku dynamicznym. Czasami zdarzało mi się łapać 0, próbując złapać None w Pythonie, co czasami może być dość trudne do wykrycia.
  • Ruby nie jest wartością odstającą. Ruby bierze to z Lispa (Ruby jest nawet potajemnie nazywany ” MatzLisp „). Lisp jest głównym językiem w informatyce. Zero jest również prawdziwą wartością w powłoce POSIX, ponieważ jest ' fragmentem tekstu: if [ 0 ] ; then echo this executes ; fi. Wartość fałszywych danych jest pustym ciągiem, a testowalnym fałszem jest stan nieudanego zakończenia polecenia, który jest reprezentowany przez inne niż -zero.

Odpowiedź

Przed omówieniem ogólnego przypadku możemy omówić przykłady liczników.

Porównania ciągów

W rzeczywistości 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 przez strcmp jest nie wartością logiczną, jest to odległość i że to, co pułapki na nieświadomych programistów robi if (strcmp(...)) do_when_equal() else do_when_not_equal().

W C ++ możemy przeprojektować strcmp, aby zwrócić Distance, który zastępuje operator bool(), aby zwracał prawdę, gdy 0 (ale wtedy zostałbyś ugryziony przez inny zestaw problemów). Lub w zwykłym C po prostu użyj funkcji streq, która zwraca 1, gdy ciągi są równe, a 0 w przeciwnym razie.

Wywołania API / kod zakończenia programu

Tutaj interesuje Cię powód, dla którego coś poszło nie tak, ponieważ spowoduje to podjęcie decyzji w przypadku błędu. Kiedy coś się powiedzie, nie chcesz wiedzieć nic konkretnego – twój zamiar zostaje zrealizowany. Wartość zwracana musi zatem przekazywać tę informację.To nie wartość logiczna, jest 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, który często oznacza „nieokreślony błąd”).

Przypadek ogólny

Pozostaje nam pytanie: dlaczego wartości logiczne są True i False zwykle reprezentowane odpowiednio przez 1 i 0?

Cóż, poza subiektywnym argumentem „Tak czuje się lepiej”, oto kilka powodów (również subiektywnych), które przychodzą mi do głowy:

  • analogia obwodu elektrycznego. Prąd jest włączony przez 1 s i wyłączony przez 0 s. Lubię mieć razem (1, Tak, Prawda, Wł.) I (0, Nie, Fałsz, Wył), a nie inny miks.

  • inicjalizacje pamięci. Kiedy memset(0) kilka zmiennych (czy to int, floats, bools), chcę, aby ich wartość odpowiadała najbardziej konserwatywnym założeniom. Na przykład. moja suma początkowo wynosi 0, predykat jest fałszywy itd.

Może wszystkie te powody są związane z moim wykształceniem – gdybym był nauczony kojarzyć 0 z True z Na początku poszedłbym na odwrót.

Komentarze

  • W rzeczywistości istnieje przynajmniej jeden język programowania, który traktuje 0 jako prawdę. Powłoka unixa.
  • +1 do rozwiązania prawdziwego problemu: większość pytań dotyczących Morwenn ' nie jest ' t w ogóle bool.
  • @ dan04 Tak jest. Cały post dotyczy uzasadnienia wyboru rzutowania z int do bool w wielu językach programowania. Porównanie i gesty błędów to tylko przykłady miejsc, w których przesłanie ich w inny sposób niż to, które ' wykonuje obecnie, miałoby sens.

Odpowiedź

Z ogólnej perspektywy mówisz o trzech całkiem różnych typach danych:

  1. Wartość logiczna. Konwencja matematyczna w algebrze Boolea to użycie 0 dla false i 1 dla true, więc warto przestrzegać tej konwencji. Myślę, że ten sposób ma również większy sens intuicyjnie.

  2. Wynik porównania. trzy wartości: <, = i > (zwróć uwagę, że żadna z nich nie jest true). Dla nich sensowne jest użycie odpowiednio wartości -1, 0 i 1 (lub, bardziej ogólnie, wartość ujemna, zero i wartość dodatnia).

    Jeśli chcesz sprawdzić równość a Jeśli masz tylko funkcję, która wykonuje ogólne porównanie, myślę, że powinieneś to wyraźnie określić, używając czegoś takiego jak strcmp(str1, str2) == 0. Uważam, że użycie ! w tej sytuacji jest mylące, ponieważ traktuje wartość inną niż boolowska tak, jakby była to wartość logiczna.

    Pamiętaj też, że porównanie i równość nie musi oznaczać tego samego. Na przykład, jeśli zamówisz ludzi według daty urodzenia, Compare(me, myTwin) powinien zwrócić 0 , ale Equals(me, myTwin) powinien zwrócić false.

  3. Powodzenie lub niepowodzenie funkcji , prawdopodobnie także ze szczegółami sukcesu lub porażki. Jeśli mówisz o systemie Windows, ten typ nazywa się HRESULT a wartość różna od zera nie musi oznaczać niepowodzenia. W rzeczywistości wartość ujemna wskazuje niepowodzenie i nieujemny sukces. Wartość sukcesu bardzo często to S_OK = 0, ale może to być na przykład S_FALSE = 1 lub inne wartości.

Nieporozumienie 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 i można użyć liczby całkowitej w warunku. Ale nie sądzę, żeby sensowne było ponowne definiowanie wartości logicznych, aby ułatwić używanie niektórych typów innych niż boolowskie w warunkach.

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

Odpowiedź

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.

Trochę psuedokodu, ponieważ publiczność prawdopodobnie nie czyta asemblera

c- proste wywołania pętli źródłowej wibble 10 razy

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

jakiś udawany zespół do tego

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 inny prosty pętla wywołuje wibble 10 razy

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

jakiś udawany asembler dla tego przypadku

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 source

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

i asembler

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

Zobacz, jakie to krótkie?

więcej źródła c

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

i assembler (załóżmy marginalnie inteligentny kompilator, który 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 source #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()

i asembler

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

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

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

Aby sprawdzić, czy a> b lub a = b zwykle pobiera instrukcję porównania.

  • Chyba że B jest albo ZERO – w takim przypadku ustawiana jest flaga ZERO Zaimplementowana jako prosty logiczny NOR lub wszystkie bity w akumulatorze.
  • Lub NEGATYWNY, w którym po prostu użyj „bitu znaku”, czyli najbardziej znaczącego bitu akumulatora jeśli używasz arytmetyki uzupełnień do dwóch. (Przeważnie to robimy)

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

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

Proszę zauważyć, że to jest pseudokod i żaden prawdziwy zestaw instrukcji nie wygląda tak. Jeśli znasz asembler, wiesz, że bardzo tu upraszczam. Jeśli wiesz cokolwiek o projektowaniu kompilatora, nie musiałeś czytać tej odpowiedzi. Każdy, kto wie cokolwiek o rozwijaniu pętli lub przewidywaniu rozgałęzień, klasa zaawansowana znajduje się na końcu korytarza w pokoju 203.

Komentarze

  • Twój punkt widzenia nie jest dobrze sformułowany, ponieważ z jednej strony if (foo) i if (foo != 0) powinien wygenerować ten sam kod, a po drugie, ' pokazujesz, że język asemblera, którego ' używasz, ma w rzeczywistości wyraźny logiczne operandy i testy dla nich. Na przykład jz oznacza jump if zero. Innymi słowy if (a == 0) goto target; . A ilość nie jest nawet bezpośrednio testowana; warunek jest konwertowany na flagę logiczną, która jest przechowywana w specjalnym słowie maszynowym. ' bardziej przypomina cpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
  • Nie Kaz, starszy procesor ' s nie działał w ten sposób. T jz / jnz można wykonać bez wykonywania instrukcji porównania. Co było właściwie celem całego mojego postu.
  • Nie ' nie napisałem nic o instrukcji porównawczej.
  • Czy możesz przytoczyć procesor, który ma instrukcję jz, ale nie ma jnz? (lub inny asymetryczny zestaw instrukcji warunkowych)

Odpowiedź

Istnieje wiele odpowiedzi, które sugerują, że zgodność między 1 a prawdą jest wymagana przez pewną właściwość matematyczną. Nie mogę znaleźć takiej własnoś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 odwzorować operacje logiczne na tym polu na dwa sposoby :

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

Równie łatwo moglibyśmy identyfikujemy Prawdę z 0, a Fałsz z 1. Następnie identyfikujemy OR z * i XNOR z +. Zatem AND jest dodawaniem nasycającym.

Komentarze

  • Jeśli podążałeś za linkiem na Wikipedii mogłeś znaleźć, że pojęcie algebry boolowskiej jest zamknięte w związku z pojęciem pola Galois złożonego z dwóch elementów ( en.wikipedia.org/wiki / GF% 282% 29 ). Symbole 0 i 1 są konwencjonalnie używane do oznaczenia tożsamości, odpowiednio, addytywnej i multiplikatywnej, ponieważ liczby rzeczywiste są również polami, których tożsamościami są liczby 0 i 1.
  • @NeilG Myślę, że Giorgio próbuje powiedzieć, że ' to coś więcej niż zwykła 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 pod względem dodawania i mnożenia.
  • @svick: Nie , ponieważ możesz po prostu zmienić nazwę mnożenia i dodawania nasycającego na OR i AND, a następnie odwrócić etykiety tak, aby 0 to prawda, a 1 to fałsz.Giorgio mówi, że była to konwencja logiki boolowskiej, która została przyjęta jako konwencja informatyki.
  • @Neil G: Nie, nie można odwrócić + i * oraz 0 i 1, ponieważ pole wymaga rozdzielności mnożenia nad dodawaniem (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 masz pola już więcej. Tak więc definiowanie przez IMO 0 i 1 jako tożsamości odpowiedniego pola wydaje się dość wiernie wychwytywać fałsz i prawdę.
  • @giorgio: Zmieniłem odpowiedź, aby było oczywiste, co się dzieje.

Odpowiedź

Co dziwne, zero nie zawsze jest fałszywe.

W szczególności konwencja Uniksa i Posixa to zdefiniowanie EXIT_SUCCESS jako 0 (i EXIT_FAILURE jako 1). W rzeczywistości jest to nawet standardowa konwencja C !

Więc dla powłok Posix i wyjście (2) wywołania systemowe, 0 oznacza „pomyślne”, co intuicyjnie jest bardziej prawdziwe niż fałszywe.

W szczególności if powłoki chce proces zwraca EXIT_SUCCESS (czyli 0), aby podążać za jego gałęzią „then”!

W schemacie (ale nie we wspólnym Lispie lub w MELT ) 0 i nil (tj. () na schemacie) są prawdziwe, ponieważ jedyną fałszywą wartością jest #f

Zgadzam się, szukam dziury!

Odpowiedź

C jest używane do programowania niskopoziomowego w pobliżu sprzętu, w obszarze, w którym czasami trzeba przełączać się między operacjami bitowymi i logicznymi na tych samych danych. Konieczność konwersji wyrażenia numerycznego na wartość logiczną tylko w celu wykonania testu spowodowałaby zagracenie kod.

Możesz napisać 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 konieczność zrobienia tego będzie uciążliwa.

Podobnie, operacje odwrotne. Dla wyniku operacji logicznej, takiej jak porównanie, przydaje się po prostu 0 lub 1: Załóżmy, że chcemy ustawić trzeci bit jakiegoś słowa na podstawie tego, czy modemctrl ma bit wykrywania nośnika:

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

Tutaj musimy mieć != 0, aby zredukować wynik wyrażenia biwise & do 0 lub 1, ale ponieważ wynik jest tylko liczbą całkowitą, oszczędzono nam konieczności dodawania irytującego rzutowania w celu dalszej konwersji wartości logicznej na liczbę całkowitą.

Mimo że współczesne C ma teraz bool, nadal zachowuje ważność takiego kodu, zarówno dlatego, że jest to „dobra rzecz”, jak i ze względu na ogromne zerwanie z kompatybilnością wsteczną, które mogłoby być spowodowane w przeciwnym razie.

Inny przykład, w którym C jest sprytne: testowanie dwóch warunków logicznych jako przełącznika czterokierunkowego:

Nie można by tego odebrać programiście C bez walki!

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

Odpowiedź

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

Powinny one nie być reprezentowane jako liczby całkowite, ale jako bity (0 i 1 ).

Powiedzenie jakiejkolwiek innej liczby całkowitej poza 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 spowodowałoby rozbicie wszystkich tablic prawdy i wszelkiej logiki logicznej z nimi związanej.

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

Większość języków ma zwykle boolean typ, który po rzutowaniu na typ liczbowy, taki jak liczba całkowita, ujawnia fałsz jako wartość całkowitą 0.

Komentarze

  • 0 AND -1 == jakąkolwiek wartość logiczną, na którą je rzutujesz. To ' o to chodzi w moim pytaniu, dlaczego przesyłam je do TRUE lub FALSE.Nigdy nie powiedziałem – może tak, ale nie było to zamierzone – liczby całkowite były prawdą lub fałszem, zapytałem, dlaczego oceniają cokolwiek po rzutowaniu na wartość logiczną.

Odpowiedź

Ostatecznie mówisz o złamaniu podstawowego języka, ponieważ niektóre interfejsy API są kiepskie. Słabe interfejsy API nie są nowe i nie można ich naprawić, psując język. Matematycznym faktem jest, że 0 jest fałszem, a 1 jest prawdą, a każdy język, który tego nie przestrzega, jest zasadniczo zepsuty. Porównanie trójetapowe to niszowe i nie ma żadnego interesu, aby jego wynik był niejawnie konwertowany na bool, ponieważ zwraca on trzy możliwe wyniki. Stare API C po prostu mają straszną obsługę błędów, a także są sparaliżowane, ponieważ C nie ma niezbędnych funkcji językowych, aby nie mieć okropnych interfejsów.

Zauważ, że nie mówię tego o językach, które nie mają ukrytych liczba całkowita-> konwersja logiczna.

Komentarze

  • ” Faktem matematycznym jest, że 0 jest fałszem a 1 jest prawdą ” Erm.
  • Czy możesz podać odniesienie do swojego ” faktu matematycznego, że 0 to fałsz a 1 to prawda „? Twoja odpowiedź brzmi niebezpiecznie jak tyrada.
  • To ' nie jest faktem matematycznym, ale ' zostało matematyczna konwencja od XIX wieku.
  • Algebra Boolea jest reprezentowana przez pole skończone, w którym 0 i 1 są elementami tożsamości dla operacji przypominających dodawanie i mnożenie. Te operacje to odpowiednio OR i AND. W rzeczywistości algebra boolowska jest napisana podobnie jak algebra normalna, w której zestawienie oznacza AND, a symbol + oznacza OR. Na przykład abc + a'b'c oznacza (a and b and c) or (a and (not b) and (not c)).

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *