Dlaczego języki nie uwzględniają implikacji jako operatora logicznego?

60

To może być dziwne pytanie, ale dlaczego w wielu językach (Java, C, C ++, Python Haskell - jako operator logiczny nie ma to żadnego znaczenia)? Uważam, że logiczna implikacja jest znacznie łatwiejsza do napisania (szczególnie w asercjach lub wyrażeniach podobnych do asercji), a następnie negacja za pomocą lub:

encrypt(buf, key, mode, iv = null) {
    assert (mode != ECB --> iv != null);
    assert (mode == ECB || iv != null);
    assert (implies(mode != ECB, iv != null)); // User-defined function
}
Maciej Piechotka
źródło
28
Ponieważ nie został umieszczony w C; gdyby został wstawiony, to posiadałyby C ++, Java i C #.
m3th0dman
4
Ale C nie ma go z kolei, ponieważ nie jest to wspólna część języka asemblera. Podczas gdy wszystkie zestawy instrukcji, które znam, obejmują i, lub nie, nie znam żadnego z nich, co sugeruje - bez wątpienia ktoś przyjdzie i opowie mi o jakimś niejasnym zestawie instrukcji, który wkrótce…
Jack Aidley
4
Być może został uznany za zbędny, ponieważ (1) jest mniej prymitywny niż „^”, „v”, „~”, (2) oszczędza bardzo mało pisania, ponieważ „A => B” jest równoważne z „~ A v B” .
Giorgio
4
Bardziej prawdopodobne jest, że języki, które zajmują się konstrukcjami logicznymi. Główną z nich jest prolog
9
Jestem zaskoczony, że uważasz, że assert# 1 jest jaśniejszy niż # 2. Przez jakiś czas wpatrywałem się w numer 1 i wciąż nie widzę, jak jego zachowanie można uznać za intuicyjne. (szczególnie z dodatkową !=logiką)
Chris Burt-Brown

Odpowiedzi:

61

Przydałoby się czasem mieć bez wątpienia. Kilka argumentów przemawia przeciwko takiemu operatorowi:

  • Postacie -i >są cenne, zarówno w izolacji, jak i razem. Wiele języków używa ich już w znaczeniu innych rzeczy. (I wielu nie może używać zestawów znaków Unicode do ich składni.)
  • Konsekwencje są bardzo sprzeczne z intuicją, nawet dla osób myślących logicznie, takich jak programiści. Fakt, że trzy z czterech wpisów w tabeli prawdy truezaskakuje wielu ludzi.
  • Wszystkie pozostałe operatory logiczne są symetryczne, co stanowiłoby ->wyjątek od ortogonalności.
  • Jednocześnie istnieje łatwe obejście: użyj operatorów !i ||.
  • Implikacja jest używana znacznie rzadziej niż logiczna and/ or.

Prawdopodobnie dlatego operator jest dziś rzadkością. Z pewnością może stać się bardziej powszechny, ponieważ nowe dynamiczne języki używają Unicode do wszystkiego, a przeciążanie operatorów znów staje się bardziej modne.

Kilian Foth
źródło
22
Twój pierwszy punkt dotyczy tylko używania „->” jako symbolu operatora, a nie faktycznego posiadania operatora. Pozostałe to dobre strony.
AShelly,
14
„Jednocześnie istnieje łatwe obejście: użyj! I &&.”: W rzeczywistości najprostszym sposobem na symulację „->” (lub „=>”, ponieważ jest to bardziej powszechne w matematyce), jest użycie !oraz ||nie !i &&: A -> Bjest równoważne z tym, !A || Bco jest równoważne z !(A && !B).
Giorgio
22
„Fakt, że trzy z czterech wpisów w tabeli prawdy są prawdziwymi, zaskakuje wielu ludzi”. Wat. Znam innego operatora z tą samą funkcją, która jest używana dość często ...
l0b0
9
@ l0b0, jestem programistą, który studiował CS, ale to było dawno temu. Zapomniałem, że „implikuje” to formalna operacja z dwoma operandami, która zwraca wartość prawdy, więc na początku byłem zdezorientowany charakterem tego pytania. W mowie codziennej używam zawsze „sugeruje”, gdy pierwszy operand jest już znany jako prawdziwy („X jest prawdą, więc Y musi być prawdą”). Po minucie przypomniałem sobie, jak formalne „implikuje” działanie, ale fakt pozostaje - nawet dla kogoś, kto kiedyś studiował logikę formalną, intencja nie była dla mnie jasna. A gdybym nigdy tego nie przestudiował, nie miałoby to nawet sensu.
Ben Lee
5
(a) W C -> jest używany jako operator. Nikt na to nie narzeka. W każdym razie użyłbym =>. (b) Dla kogo sprzeczne z intuicją? Definicja implikacji jest właśnie taka, definicja. (c) odejmowanie nie jest przemienne. Nikt na to nie narzeka. (d) Myślę, że masz na myśli! i ||. Ze względu na pierwszeństwo (i) często są również potrzebne. Ten sam argument można zastosować przeciwko && lub ||; wybierz swój. (e) Być może dzieje się tak, ponieważ nie ma go w większości języków. Wiele zastosowań || można by zwięźle i (przynajmniej dla mnie) intuicyjnie zastąpić implikacją.
Theodore Norvell,
54

Wierzę, że odpowiedź leży w matematycznych podstawach . Implikacja jest zwykle rozpatrywana i definiowana jako pochodna, a nie elementarna operacja algebr boolowskich. Języki programowania są zgodne z tą konwencją.

Oto propozycja I rozdziału II z George'a Boole'a An Investigation of the Laws of Thought (1854) :

Wszystkie operacje języka jako instrumentu rozumowania mogą być prowadzone przez system znaków złożony z następujących elementów, a mianowicie:

1. miejsce Dosłowne symbole, takie jak x, y i c., Reprezentujące rzeczy jako podmioty naszych koncepcji.

2. miejsce Znaki działania, jako +, -, ×, oznaczające te operacje umysłu, za pomocą których koncepcje rzeczy są łączone lub rozwiązywane, aby tworzyć nowe koncepcje obejmujące te same elementy.

3. miejsce Znak tożsamości, =.

A te symbole logiki są w użyciu podlegające określonym prawom, częściowo zgodnym, a częściowo różnym od praw odpowiednich symboli w nauce Algebry.

Znak Boole'a + reprezentuje tę samą „operację umysłu”, którą dzisiaj uznajemy za zarówno logiczny „lub”, jak i związek zbiorów. Podobnie znaki - i × odpowiadają naszej negacji boolowskiej, uzupełnieniu zbioru, wartości logicznej „i” oraz przecięciu zbiorów.

POCHODZIĆ Z
źródło
+1 - solidna odpowiedź. Algebra boolowska i uproszczenia logiczne, które pozwalają na wiele metodologii programowania. Użycie „implikuje ...” potencjalnie zakłóciłoby warunki „nie przejmuj się”.
3
1. To zależy od definicji. Możliwe jest zdefiniowanie lub jako a || b = !(!a && !b)(poznałem lub jako operator pochodny w logice). 2. Powodem, dla którego tak się dzieje, jest zwykle uproszczenie indukcji, jak sądzę (wiemy, że operatory pochodne są tylko skrótami, więc nie musimy mieć dla nich osobnego przypadku). @ GlenH7: Jakie warunki „nie obchodzi mnie”? a --> bjest taki sam jak !a || b.
Maciej Piechotka
3
Mamy też inne operatory pochodne, takie jak xor ( !=) nxor ( ==) ...
Maciej Piechotka,
5
W rzeczywistości wszystko można uzyskać z NAND !(a && b)lub NOR !(a || b). Na przykład NOT a == a NAND a, a AND b == NOT(a NAND b), a OR b == (NOT a) NAND (NOT b). Ale udostępnienie tylko operatora NAND umieszcza cię w dziedzinie ezoterycznych języków (co zaskakująco dobrze pasuje, biorąc pod uwagę nazwę użytkownika odpowiadającego ;-)).
Stefan Majewsky 18.01.2013
1
@Stefan: Możesz także uzyskać wszystko z IMPL.
Mason Wheeler
37

AKTUALIZACJA: To pytanie było przedmiotem mojego bloga w listopadzie 2015 r . Dzięki za interesujące pytanie!


Visual Basic 6 (i VBScript) miał Impi Eqvoperatory. Nikt ich nie używał. Oba zostały usunięte w VB.NET; według mojej wiedzy nikt nie narzekał.

Przez cały rok pracowałem w zespole kompilatorów Visual Basic (jako stażysta) i nigdy nie zauważyłem, że VB ma nawet tych operatorów. Gdybym nie był facetem piszącym kompilator VBScript i dlatego musiałem przetestować ich implementacje, prawdopodobnie nie zauważyłbym ich. Jeśli facet w zespole kompilatorów nie wie o tej funkcji i jej nie używa, to mówi ci coś o popularności i przydatności tej funkcji.

Wspomniałeś o językach podobnych do C. Nie mogę mówić do C, C ++ lub Java, ale mogę mówić do C #. Istnieje lista funkcji sugerowanych przez użytkownika dla C # dosłownie dłuższych niż twoje ramię. Według mojej wiedzy, żaden użytkownik nigdy nie zaproponował takiego operatora zespołowi C #, a ja przeglądałem tę listę wiele, wiele razy.

Funkcje, które istnieją i są nieużywane w jednym języku, i nigdy nie są wymagane w innym, są mało prawdopodobnymi kandydatami do przejścia na nowoczesne języki programowania. Szczególnie, gdy nie ma wystarczająco dużo czasu i wysiłku i pieniędzy dostępnych w budżecie, aby możliwości, że ludzie nie chcą.

Eric Lippert
źródło
IMP wraca jako operator do dni GWBASIC. Pamiętam to z 1979 roku.
zarchasmpgmr
1
Czytałem, że IMPL jest bardzo pomocny w przypadku kontraktów kodowych. (Którego VB nie miał.)
Mason Wheeler
3
Programiści VBScript byli prawdopodobnie grupą najmniej skłonną do korzystania z takiego operatora. Chciałbym teraz, aby PowerShell miał domyślny operator, dzięki czemu mógłbym łatwiej i czytelniej zaimplementować niektóre [switch]parametry filtrów.
brianary
Pamiętam IMP z DEC BASIC. Nie pamiętam EQV. Operator, o którym często marzyłem, byłby „i nie”, który promowałby operatora po prawej stronie przed jego zanegowaniem. W ten sposób 0xABCD12345678ul ~& 0x00200000udałoby 0xABCD12145678ul zamiast 0x12145678ul.
supercat
19

Mogę tylko zgadywać, ale powodem może być to, że istnieją obejścia, które są dość proste i czytelne. Rozważmy twój przykład:

if (mode != ECB) assert (iv != null);

assert (mode != ECB --> iv != null);  // <-- that's only one character shorter

Jeśli potrzebujesz wyrażenia, można użyć wbudowanego operatora dostępnego w większości języków (często nazywanego operatorem potrójnym). Oczywiście nie jest to tak eleganckie A --> B, ale liczba przypadków użycia może nie uzasadniać dodania (i utrzymania!) Innego operatora:

assert (mode != ECB ? iv != null : true);
Heinzi
źródło
2
Co do zapewnień - jest dobry powód. Pierwszy wygeneruje (w wielu implementacjach) komunikat „błąd potwierdzenia: iv! = Zero”, a drugi „błąd potwierdzenia: tryb! = ECB -> iv! = Zero”.
Maciej Piechotka,
2
+1, miałem właśnie zadać to samo pytanie (ponieważ nie znam się zbyt dobrze na „implikacji”, to nigdy nie pojawia się z dnia na dzień). To ifstwierdzenie jest o wiele wyraźniejsze dla wszystkich.
Izkata,
12

Eiffel ma znaczenie

W rzeczywistości ma jeszcze więcej. Ma wiele pół-ścisłych operatorów, a także ścisłych.

Programiści nie używają takich rzeczy, ponieważ nigdy nie są szkoleni, aby dokładnie wiedzieć, czym są, jak z nich korzystać i kiedy ich używać - a także jak projektować z nimi. Ponieważ nigdy nie są szkoleni, nigdy nie proszą o to pisarzy kompilatorów, dlatego ludzie kompilatorów nie zawracają sobie głowy umieszczaniem takich mechanizmów w kompilatorze. Kiedy studenci informatyki i programiści cieni zaczną zdobywać bardziej zaokrąglone wykształcenie, kompilatory zaczną nadrabiać zaległości.

Okazuje się, że kiedy masz język z takimi operatorami boolowskimi i wiesz, jak z nimi projektować i korzystać z nich, wtedy ich używasz.

W Eiffelu użycie słowa kluczowego „implikuje” jest dość widoczne ze względu na projektowanie według umowy ze względu na boolowską naturę twierdzeń o umowie. Istnieje kilka umów, które można poprawnie i skutecznie napisać tylko z operatorem „implikującym”. To pociąga za sobą komentarz, że języki bez umów są dalej bez powodu, aby patrzeć, szkolić i wdrażać stosowanie implikacji.

Dodaj do tego, że większość programistów jest „słaba z matematyki i logiki”, opowiada nam resztę historii. Nawet jeśli w swoim wykształceniu masz duże matematykę i logikę, kiedy wybierzesz język, który nie implementuje konstrukcji takich jak implikacja, wówczas myślisz, że takie rzeczy są niepotrzebne lub nieprzydatne. Rzadko kwestionuje się język i przechodzi do echa: „Cóż, kompilatorzy nie widzą potrzeby” i „Cóż, programiści nie widzą potrzeby” - niekończące się i błędne koło.

Zamiast tego ludzie kompilatora muszą wykonać kopię zapasową teorii, napisać notację językową sugerowaną lub sugerowaną przez teorię (np. Teorię obiektową), niezależnie od tego, o czym myślą lub proszą niemyte masy programistów. Stamtąd profesorowie, nauczyciele i inni specjaliści muszą fachowo szkolić młode papugi w oparciu o surową teorię, a NIE „teorię poprzez soczewki językowe”. Kiedy tak się stanie, ludzie nagle się obudzą i uświadomią sobie, czego im brakowało i co zostało im narzucone.

W tej chwili - jest tyle teorii, które podszywają się pod obiektowe, ale to tylko OO-przez-szklankę-ciemno-z-[wybierz swój język]. Nie można przeczytać większości „teorii” książek na temat OO, ponieważ chcą interpretować teorię przez pryzmat jakiegoś języka. Całkowicie fałszywe i niepoprawne. To byłoby jak nauczanie matematyki opartej na moim kalkulatorze lub mojej reguły slajdów. NIE - człowiek pozwala rzeczywistości uczyć się o sobie, a następnie używa notacji do opisania tego, co obserwuje - nazywa się to „nauką”. Ten inny zacier o nazwie X oparty na języku OO jest tak wypaczony, że ledwo reprezentuje rzeczywistość.

Więc odejdź od języka, spójrz na surową teorię i zacznij od nowa. Nie pozwól, aby ograniczenia, ograniczenia i malowanie w języku mówiły ci o teorii. Po prostu pozwól, aby rzeczywistość teorii podyktowała swój własny zapis, a następnie stamtąd przejdź do sformułowania języka.

Od tego momentu zaczniesz rozumieć, w jaki sposób implikacja i „implikacja” jest nie tylko przydatna, ale także elegancka i bardzo fajna!

Miłego!

Larry
źródło
7

Python ma operator implikacji w sposób ukryty.

Operator mniejszy lub równy jest przeciążony, aby zaakceptować wartości logiczne. W rezultacie można go używać jako operatora implikacji.

Dowód według przykładu (kod Python):

print (False <= False) # This will print True.
print (False <= True)  # This will print True.
print (True <= False)  # This will print False.
print (True <= True)   # This will print True.

Przypomnijmy, że tabela prawdy operatora implikacji to:

LEFT RIGHT RESULT
F    F     T
F    T     T
T    F     F
T    T     T
Mackenzie
źródło
2
Jest to niesamowite, ale niestety ma złe właściwości ścisłości: f() implies g()nie powinno oceniać, g()czy f()jest False, ale <=ocenia g()w tym przypadku.
Jon Purdy,
2
@Jon Purdy - Ściśle mówiąc, ocena zwarcia jest opcjonalną funkcją operatorów binarnych. Tak więc technicznie jest to całkowicie uzasadniona implementacja operatora implikacji. Biorąc to pod uwagę, naprawdę wątpię, aby Guido van Rossum nawet zamierzał być operatorem implikacji. To tylko dziwactwo porównywania booleanów.
Mackenzie,
Jasne, mówiłem, że to ciekawe spostrzeżenie, ale nie jest to coś, czego powinieneś używać w prawdziwych programach, ponieważ nie powoduje to zwarcia i które mogłyby zaskoczyć kogoś, gdyby oczekiwał, że did_all_possible_stuff = x.do_required_stuff() and (x.supports_optional_stuff() <= x.do_optional_stuff())zadziała.
Jon Purdy
2
Działa to również w C i C ++.
Ben Voigt
5

Eiffel ma operatora „implikującego” i jest bardzo przyjemny w pisaniu warunków wstępnych i dodatkowych. Sprawia, że ​​kod jest bardziej czytelny, niestety to nigdy nie było podstawową intencją języka podobnego do C i zbyt mało osób używało Eiffla, więc piękno „sugeruje”, ponieważ operator nie jest dobrze znany.

Lothar
źródło
2

Dla mnie duży problem z implikacjami pojawia się, gdy warunek początkowy jest fałszywy; na przykład: „Jeśli słońce jest zielone, jestem nietoperzem owocowym”. Prawdziwe!

W logice formalnej po prostu działa przypisanie wartości „True” do wartości, gdy warunek implikacji nie jest spełniony. Ale w programowaniu może to prowadzić do sprzecznych z intuicją i zaskakujących wyników.

Wydaje mi się, że przykład podany powyżej przez Heinzi:

if (sun.color() == "green") then assert(me.species() == "fruitbat")

Jest znacznie łatwiejszy do zrozumienia i mniej podatny na błędy z powodu niezrozumienia logiki.

W kontekście testowania po prostu zatrzymałbym się, gdy moje założenia stały się fałszywe:

assert(sun.color() == "green")
assert(me.species() == "fruitbat")

Tak więc, do rzeczy @Eric Lippert, jako programista nie wiem, kiedy użyłbym domyślnego operatora. Zachowanie „False is True” wydaje się przydatne w kontekście matematycznej, formalnej logiki, ale może prowadzić do nieoczekiwanych rezultatów w logice programu.

...

Nikt nie wspominał o zwykłym „?” operator, gdzie „A? B: prawda” jest tym samym, co „implikuje”. Ale podczas gdy logika formalna przypisuje prawdę warunkowi „else”, „?” pozwala programistom przypisać to wprost.

W logice formalnej sprawdza się użycie „true”, ale w programowaniu, jeśli warunek jest fałszywy, wówczas lepsza odpowiedź jest bardziej jak „null” lub „void” lub „skip”, a może nawet false.

Myślę, że ktoś musiałby uzasadnić, dlaczego „implikuje” jest bardziej użytecznym operatorem niż „?”, Instrukcje „jeśli” lub zwykły przepływ programu.

Obrabować
źródło