Dlaczego większość języków głównego nurtu nie obsługuje składni „x <y <z” dla 3-kierunkowych porównań boolowskich?

34

Gdybym chciał porównać dwie liczby (lub inne dobrze uporządkowane byty), zrobiłbym to z x < y. Jeśli chcę porównać trzy z nich, licealistka z algebry zasugeruje spróbowanie x < y < z. Programista we mnie odpowie wtedy „nie, to nieważne, musisz to zrobić x < y && y < z”.

Większość języków, z którymi się spotkałem, nie obsługuje tej składni, co jest dziwne, biorąc pod uwagę, jak często występuje w matematyce. Python jest godnym uwagi wyjątkiem. JavaScript wygląda jak wyjątek, ale tak naprawdę jest to po prostu niefortunny produkt uboczny pierwszeństwa operatora i niejawnych konwersji; w node.js, 1 < 3 < 2ocenia true, ponieważ tak naprawdę jest (1 < 3) < 2 === true < 2 === 1 < 2.

Moje pytanie brzmi zatem: dlaczego x < y < znie jest powszechnie dostępne w językach programowania z oczekiwaną semantyką?

JesseTG
źródło
1
Oto plik gramatyki, który łatwo trzymają w dokumentacji Pythona - nie sądzę, że to takie trudne: docs.python.org/reference/grammar.html
Aaron Hall
Nie znam innych języków tak dobrze jak Python, ale mogę mówić o prostocie jego interpretacji. Może powinienem odpowiedzieć. Ale nie zgadzam się z wnioskiem gnasher729, że wyrządza on szkody.
Aaron Hall
@ErikEidt - Zapotrzebowanie na umiejętność pisania wyrażeń matematycznych w sposób, w jaki nas uczono w szkole średniej (lub wcześniej). Każdy, kto ma matematyczne skłonności, wie, co oznacza $ a <b <c <d $. To, że istnieje funkcja, nie oznacza, że ​​musisz jej użyć. Ci, którym się to nie podoba, zawsze mogą wprowadzić zasadę osobistą lub projektową zakazującą jej używania.
David Hammen
2
Myślę, że sprowadza się to do tego, że lepiej jest zespołowi C # (na przykład) zbadać LINQ, a w przyszłości być może typy rekordów i dopasowanie wzorców niż dodawanie cukru syntaktycznego, który zaoszczędziłby ludziom 4 naciśnięcia klawiszy, a tak naprawdę dodaj wyrazistości (możesz także napisać metody pomocy, static bool IsInRange<T>(this T candidate, T lower, T upper) where T : IComparable<T>jeśli naprawdę przeszkadza ci zobaczyć &&s)
sara
1
SQL jest dość „głównym nurtem” i można pisać „x od 1 do 10”
JoelFan

Odpowiedzi:

30

Są to operatory binarne, które po połączeniu łańcuchowym normalnie i naturalnie wytwarzają abstrakcyjne drzewo składniowe, takie jak:

normalne abstrakcyjne drzewo składniowe dla operatorów binarnych

Po dokonaniu oceny (co robisz od początku do końca), daje to wynik boolowski x < y, a następnie pojawia się błąd typu boolean < z. Aby x < y < zdziałać zgodnie z opisem, musisz utworzyć specjalny przypadek w kompilatorze, aby utworzyć drzewo składniowe, takie jak:

drzewo składni specjalnych przypadków

Nie to, że nie można tego zrobić. Oczywiście jest, ale dodaje złożoności parserowi dla sprawy, która tak naprawdę nie pojawia się tak często. Zasadniczo tworzysz symbol, który czasami działa jak operator binarny, a czasem skutecznie działa jak operator trójskładnikowy, ze wszystkimi implikacjami obsługi błędów i takimi, które się z tym wiążą. Daje to dużo miejsca na rzeczy, które mogą się nie udać, których projektanci języka woleliby raczej uniknąć, jeśli to możliwe.

Karl Bielefeldt
źródło
1
"wtedy pojawia się błąd typu przy próbie wykonania logicznej wartości <z" - nie, jeśli kompilator pozwala na tworzenie łańcuchów poprzez ocenę y w miejscu dla porównania z. „Daje to dużo miejsca na rzeczy, które mogą się nie udać, których projektanci języków woleliby uniknąć, jeśli to możliwe. W rzeczywistości Python nie ma z tym problemu, a logika analizy jest ograniczona do jednej funkcji: hg.python.org/cpython/file/tip/Python/ast.c#l1122 - nie ma dużo miejsca na rzeczy do zrobienia źle. „czasami działa jak operator binarny, a czasem skutecznie działa jak operator trójkowy”. W Pythonie cały łańcuch porównania jest trójskładnikowy.
Aaron Hall
2
Nigdy nie powiedziałem, że nie jest to wykonalne, po prostu dodatkowa praca z dodatkową złożonością. Inne języki nie trzeba pisać żadnego odrębnego kodu dla obsługi swoich operatorów porównania. Dostajesz go za darmo z innymi operatorami binarnymi. Musisz tylko określić ich pierwszeństwo.
Karl Bielefeldt
Tak, ale ... nie jest już Ternary operator dostępny w wielu językach?
JensG
1
@JensG Oznaczenie potrójnego oznacza, że ​​wymaga 3 argumentów. W kontekście linku jest to trójskładnikowy operator warunków. Najwyraźniej „trójwymiarowy” w ujęciu wymyślonym dla operatora, który wydaje się brać 2, ale faktycznie bierze 3. Moim głównym problemem z tą odpowiedzią jest to, że FUD.
Aaron Hall
2
Jestem jednym z zwolenników tej zaakceptowanej odpowiedzi. (@JesseTG. Proszę unaccept tej odpowiedzi) To pytanie mylony jakie x<y<zśrodki, lub co ważniejsze x<y<=z. Ta odpowiedź interpretuje x<y<zjako operator trójkowy. Właśnie tak nie należy interpretować tego dobrze zdefiniowanego wyrażenia matematycznego. x<y<zjest zamiast tego skrótem (x<y)&&(y<z). Poszczególne porównania są wciąż binarne.
David Hammen
37

Dlaczego x < y < znie jest powszechnie dostępny w językach programowania?

W tej odpowiedzi stwierdzam, że

  • chociaż ten konstrukt jest trywialny do wdrożenia w gramatyce języka i stanowi wartość dla użytkowników języka,
  • główne powody, dla których nie występuje w większości języków, wynikają z jego znaczenia w stosunku do innych cech oraz niechęci organów zarządzających językami do
    • zdenerwować użytkowników potencjalnie niszczącymi zmianami
    • przejść do wdrożenia funkcji (tj. lenistwo).

Wprowadzenie

Potrafię mówić na ten temat z perspektywy Pythona. Jestem użytkownikiem języka z tą funkcją i lubię studiować szczegóły implementacji tego języka. Poza tym jestem nieco zaznajomiony z procesem zmiany języków, takich jak C i C ++ (norma ISO jest regulowana przez komitet i wersjonowana z roku na rok). Widziałem, jak zarówno Ruby, jak i Python wdrażają przełomowe zmiany.

Dokumentacja i implementacja Pythona

Z dokumentacji / gramatyki wynika, że ​​możemy połączyć dowolną liczbę wyrażeń za pomocą operatorów porównania:

comparison    ::=  or_expr ( comp_operator or_expr )*
comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "!="
                   | "is" ["not"] | ["not"] "in"

a dokumentacja zawiera ponadto:

Porównania można łączyć dowolnie, np. X <y <= z jest równoważne x <y i y <= z, z tym wyjątkiem, że y jest oceniane tylko raz (ale w obu przypadkach z nie jest w ogóle oceniane, gdy x <y zostanie znalezione być fałszywym).

Równoważność logiczna

Więc

result = (x < y <= z)

logicznie równoważne pod względem oceny x, yoraz zz tym wyjątkiem, że yjest wyznaczana podwójnie:

x_lessthan_y = (x < y)
if x_lessthan_y:       # z is evaluated contingent on x < y being True
    y_lessthan_z = (y <= z)
    result = y_lessthan_z
else:
    result = x_lessthan_y

Znowu różnica polega na tym, że y jest oceniane tylko raz (x < y <= z).

(Uwaga: nawiasy są całkowicie niepotrzebne i zbędne, ale użyłem ich dla korzyści pochodzących z innych języków, a powyższy kod jest całkiem legalnym Pythonem).

Sprawdzanie przeanalizowanego abstrakcyjnego drzewa składni

Możemy sprawdzić, w jaki sposób Python analizuje łańcuchowe operatory porównania:

>>> import ast
>>> node_obj = ast.parse('"foo" < "bar" <= "baz"')
>>> ast.dump(node_obj)
"Module(body=[Expr(value=Compare(left=Str(s='foo'), ops=[Lt(), LtE()],
 comparators=[Str(s='bar'), Str(s='baz')]))])"

Widzimy więc, że tak naprawdę nie jest to trudne do przeanalizowania przez Python ani żaden inny język.

>>> ast.dump(node_obj, annotate_fields=False)
"Module([Expr(Compare(Str('foo'), [Lt(), LtE()], [Str('bar'), Str('baz')]))])"
>>> ast.dump(ast.parse("'foo' < 'bar' <= 'baz' >= 'quux'"), annotate_fields=False)
"Module([Expr(Compare(Str('foo'), [Lt(), LtE(), GtE()], [Str('bar'), Str('baz'), Str('quux')]))])"

I w przeciwieństwie do obecnie przyjętej odpowiedzi, operacja trójskładnikowa jest ogólną operacją porównania, która wymaga pierwszego wyrażenia, iteracji określonych porównań i iteracji węzłów wyrażeń w celu oceny w razie potrzeby. Prosty.

Wnioski dotyczące Pythona

Osobiście uważam, że semantyka zakresu jest dość elegancka, a większość specjalistów Pythona, których znam, zachęciłaby do korzystania z tej funkcji, zamiast uważać ją za szkodliwą - semantyka jest dość wyraźnie określona w dobrze znanej dokumentacji (jak wspomniano powyżej).

Zauważ, że kod jest odczytywany znacznie więcej niż jest napisany. Zmiany, które poprawiają czytelność kodu, powinny zostać uwzględnione, a nie zdyskontowane poprzez podniesienie ogólnych spektrów lęku, niepewności i wątpliwości .

Dlaczego więc x <y <z nie jest powszechnie dostępny w językach programowania?

Myślę, że istnieje zbieżność przyczyn, które koncentrują się wokół względnej ważności cechy i względnego pędu / bezwładności zmiany dozwolonej przez gubernatorów języków.

Podobne pytania można zadać na temat innych ważniejszych funkcji językowych

Dlaczego wielokrotne dziedziczenie nie jest dostępne w Javie lub C #? Nie ma dobrej odpowiedzi, żeby którekolwiek z tych pytań . Być może deweloperzy byli zbyt leniwi, jak twierdzi Bob Martin, a podane powody są jedynie wymówką. Wielokrotne dziedziczenie jest dość dużym tematem w informatyce. Jest to z pewnością ważniejsze niż tworzenie łańcuchów przez operatora.

Istnieją proste obejścia

Łańcuch operatorów porównania jest elegancki, ale w żadnym wypadku nie jest tak ważny jak wielokrotne dziedziczenie. I podobnie jak Java i C # mają interfejsy jako obejście, tak też każdy język dla wielu porównań - po prostu łączysz porównania z logicznymi „i”, które działają wystarczająco łatwo.

Większość języków jest regulowana przez komitet

Większość języków ewoluuje według komisji (zamiast mieć rozsądnego Dobrotliwego Dyktatora Życia, tak jak Python). I spekuluję, że ta kwestia po prostu nie spotkała się z wystarczającym poparciem, aby wyjść z odpowiednich komisji.

Czy języki, które nie oferują tej funkcji, mogą się zmienić?

Jeśli język na to pozwala x < y < zbez oczekiwanej semantyki matematycznej, byłaby to przełomowa zmiana. Gdyby przede wszystkim na to nie pozwalał, dodanie tego byłoby prawie trywialne.

Przełamywanie zmian

Jeśli chodzi o języki z przełomowymi zmianami: aktualizujemy języki z przełomowymi zmianami zachowania - ale użytkownikom się to nie podoba, szczególnie użytkownikom funkcji, które mogą być zepsute. Jeśli użytkownik polega na poprzednim zachowaniu x < y < z, prawdopodobnie głośno zaprotestowałby. A ponieważ większość języków jest zarządzana przez komisję, wątpię, byśmy mieli dużo woli politycznej, aby poprzeć taką zmianę.

Aaron Hall
źródło
Szczerze mówiąc, nie podejmuje żadnych problemu z semantyki języków dostarczonych przez operacje porównywania łańcucha takie jak `x <y <z` ale jest trywialny dla dewelopera psychicznie map x < y < zdo (x < y) && (y < z). Wybierając gnidy, mentalnym modelem powiązanego porównania jest ogólna matematyka. Klasyczne porównanie nie jest ogólnie matematyką, ale logiką logiczną. x < ydaje odpowiedź binarną {0}. y < zdaje odpowiedź binarną {1}. {0} && {1}daje opisową odpowiedź. Logika jest złożona, a nie naiwnie powiązana.
K. Alan Bates
Aby lepiej się komunikować, poprzedziłem odpowiedź jednym zdaniem, które bezpośrednio podsumowuje całą treść. To długie zdanie, więc rozbiłem je na strzały.
Aaron Hall
2
Kluczowym powodem, dla którego kilka języków implementuje tę funkcję, jest to, że przed Guido nikt nawet o tym nie myślał. Języki, które dziedziczą po C, nie mogą teraz uzyskać tego „właściwego” (matematycznie poprawnego) teraz przede wszystkim dlatego, że twórcy języka C „źle” (matematycznie źle) ponad 40 lat temu. Istnieje wiele kodów, które zależą od sprzecznego z intuicją charakteru interpretacji tych języków x<y<z. Język ma kiedyś szansę na uzyskanie czegoś takiego w odpowiedni sposób, a ta jedna szansa jest na początku jego istnienia.
David Hammen
1
@ K.AlanBates Robisz 2 punkty: 1) łańcuch operatorów jest niechlujny i 2) że cukier składniowy nie ma wartości. Po pierwsze: wykazałem, że łączenie operatorów jest w 100% deterministyczne, prawda? Być może niektórzy programiści są zbyt leniwi umysłowo, aby rozszerzyć swoje umiejętności rozumienia konstrukcji? Do drugiej kwestii: brzmi dla mnie tak, jakbyś bezpośrednio sprzeciwił się czytelności? Czy cukier syntaktyczny zwykle nie jest uważany za dobry, gdy poprawia czytelność? Jeśli myślenie w ten sposób jest normalne, dlaczego programista nie chciałby się w ten sposób komunikować? Kod powinien być napisany do przeczytania, nie?
Aaron Hall
2
I have watched both Ruby and Python implement breaking changes.Dla tych, którzy są ciekawi, oto przełomowa zmiana w C # 5.0 obejmująca zmienne i zamknięcia pętli: blogs.msdn.microsoft.com/ericlippert/2009/11/12/…
user2023861
13

Języki komputerowe próbują zdefiniować najmniejsze możliwe jednostki i pozwalają je łączyć. Najmniejszą możliwą jednostką byłoby coś w rodzaju „x <y”, co daje wynik logiczny.

Możesz poprosić o operatora trójskładnikowego. Przykładem może być x <y <z. Jakie kombinacje operatorów są dozwolone? Oczywiście x> y> z lub x> = y> = z lub x> y> = z, a może x == y == z powinno być dozwolone. Co z x <y> z? x! = y! = z? Co oznacza ostatni, x! = Y i y! = Z lub że wszystkie trzy są różne?

Teraz promocje argumentów: W C lub C ++ argumenty będą promowane do wspólnego typu. Co więc oznacza, że ​​x <y <z oznacza x jest podwójne, ale y i z są długimi długimi int? Wszystkie trzy awansowały na podwojenie? Czy y jest brany podwojony raz, a tak długo długi drugi raz? Co się stanie, jeśli w C ++ jeden lub oba operatory zostaną przeciążone?

I na koniec, czy dopuszczasz dowolną liczbę operandów? Jak <b> c <d> e <f> g?

Cóż, wszystko się komplikuje. Teraz nie miałbym nic przeciwko, że x <y <z powoduje błąd składniowy. Ponieważ jego przydatność jest niewielka w porównaniu do szkód wyrządzonych początkującym, którzy nie mogą zrozumieć, co faktycznie robi x <y <z.

gnasher729
źródło
4
Krótko mówiąc, trudno jest dobrze zaprojektować.
Jon Purdy
2
To nie jest tak naprawdę powód, aby wyjaśniać, dlaczego żaden dobrze znany język nie zawiera tej funkcji. W rzeczywistości dość łatwo jest umieścić go w języku w dobrze zdefiniowany sposób. Wystarczy spojrzeć na to jako listę połączoną przez operatorów podobnego typu, a nie każdy operator binarny. To samo można zrobić dla sum x + y + z, z tą różnicą, że nie oznacza to żadnej różnicy semantycznej. Po prostu żaden dobrze znany język nigdy nie chciał tego zrobić.
cmaster
1
Myślę, że w Pythonie jest to trochę optymalizacja ( x < y < zrównoważna, ((x < y) and (y < z))ale z yocenianą tylko raz ), którą wyobrażam sobie, że skompilowane języki optymalizują się. „Ponieważ jego przydatność jest niewielka w porównaniu do szkód wyrządzonych początkującym, którzy nie mogą zrozumieć, co faktycznie robi x <y <z.” Myślę, że to niezwykle przydatne. Prawdopodobnie dostanie -1 za to ...
Aaron Hall
Jeśli naszym celem jest zaprojektowanie języka, który eliminuje wszystkie rzeczy, które mogą być mylące dla najbardziej głupich programistów, taki język już istnieje: COBOL. Wolałbym raczej używać pytona, w którym rzeczywiście można pisać a < b > c < d > e < f > g, o „oczywistym” znaczeniu (a < b) and (b > c) and (c < d) and (d > e) and (e < f) and (f > g). To, że umiesz pisać, nie oznacza, że ​​powinieneś. Eliminacja takich potworności należy do przeglądu kodu. Z drugiej strony pisanie 0 < x < 8w pythonie ma oczywiste (bez przestraszonych cytatów), co oznacza, że ​​x leży między 0 a 8, wyłącznie.
David Hammen
@DavidHammen, jak na ironię, COBOL rzeczywiście dopuszcza <b <c
JoelFan
10

W wielu językach programowania x < yjest wyrażeniem binarnym, które akceptuje dwa operandy i zwraca jeden wynik boolowski. Dlatego, jeśli połączenie wielu wyrażeń będzie łańcuchowe true < zi false < znie będzie miało sensu, a jeśli wyrażenia te zostaną pomyślnie ocenione, prawdopodobnie wygenerują zły wynik.

Jest dużo łatwiej myśleć x < yjako wywołanie funkcji , która przyjmuje dwa parametry i generuje pojedynczy wynik logiczną. W rzeczywistości tyle języków implementuje to pod maską. Jest łatwy do skompilowania i po prostu działa.

x < y < zScenariusz jest znacznie bardziej skomplikowana. Teraz kompilator, w efekcie musi mody trzy funkcje: x < y, y < z, a wynik z tych dwóch wartości anded razem, wszystko w kontekście zapewne gramatyki języka dwuznaczny .

Dlaczego zrobili to w drugą stronę? Ponieważ jest to jednoznaczna gramatyka, o wiele łatwiejsza do wdrożenia i o wiele łatwiejsza do poprawienia.

Robert Harvey
źródło
2
Jeśli projektujesz język, masz szansę, aby uczynić go właściwym rezultatem.
JesseTG 27.04.16
2
Oczywiście, że odpowiada na pytanie. Jeśli pytanie jest naprawdę , dlaczego , odpowiedź brzmi „bo to co projektanci wybrali język.” Jeśli możesz znaleźć lepszą odpowiedź, wybierz ją. Zauważ, że Zgrzytacz powiedział dokładnie to samo w pierwszym akapicie swojej odpowiedzi .
Robert Harvey
3
Znowu dzielisz włosy. Programiści zwykle to robią. „Czy chcesz wynieść śmieci?” "Nie." „Czy wyniesiesz śmieci?” "Tak."
Robert Harvey
2
Ja również kwestionuję ostatni akapit. Python obsługuje porównania łańcuchów, a jego parser to LL (1). To nie koniecznie trudne do zdefiniowania i wdrożenia semantykę albo: Python po prostu mówi, żee1 op1 e2 op2 e3 op3 ... jest równoważne e1 op e2 and e2 op2 e3 and ...z tym że każdy wyraz jest oceniany tylko raz. (BTW, ta prosta reguła ma mylący efekt uboczny, że takie stwierdzenia a == b is Truenie mają już zamierzonego efektu.)
2
@RobertHarvey re:answer tym mój umysł natychmiast poszedł do mojego komentarza na temat głównego pytania. Nie uważam wsparcia dla x < y < zdodawania jakiejkolwiek konkretnej wartości do semantyki języka. (x < y) && (y < z)jest szerzej wspierany, jest bardziej wyraźny, bardziej wyrazisty, łatwiejszy do przyswojenia w jego składowych, bardziej składalny, bardziej logiczny, łatwiejszy do refaktoryzacji.
K. Alan Bates
6

Większość języków głównego nurtu jest (przynajmniej częściowo) zorientowana obiektowo. Zasadniczo podstawową zasadą OO jest to, że obiekty wysyłają wiadomości do innych obiektów (lub do siebie), a odbiorca tej wiadomości ma pełną kontrolę nad sposobem odpowiedzi na tę wiadomość.

Zobaczmy teraz, jak moglibyśmy wprowadzić coś takiego

a < b < c

Możemy to ocenić ściśle od lewej do prawej (lewostronne):

a.__lt__(b).__lt__(c)

Ale teraz wzywamy __lt__do wyniku a.__lt__(b), który jest Boolean. To nie ma sensu.

Spróbujmy odpowiednio skojarzyć:

a.__lt__(b.__lt__(c))

Nie, to też nie ma sensu. Teraz mamy a < (something that's a Boolean).

Okej, a co z traktowaniem go jak cukru syntaktycznego? Zróbmy łańcuch n< porównań i wyślij wiadomość n-1-ary. Może to oznaczać, możemy wysłać wiadomość __lt__do a, przechodząc bi cjako argumenty:

a.__lt__(b, c)

Okej, to działa, ale tutaj jest dziwna asymetria: a decyduje, czy jest mniejsza niż b. Ale bnie ma się zdecydować, czy jest ona mniejsza niż c, zamiast tego, że decyzja jest również wykonany przez a.

A co z interpretowaniem go jako wiadomości n-ary wysyłanej do this?

this.__lt__(a, b, c)

Wreszcie! To może działać. Oznacza to jednak, że kolejność obiektów nie jest już własnością obiektu (np. Czy ajest mniejsza niż bżadna z właściwościa ani z b), ale zamiast tego jest właściwością kontekstu (tj this.).

Z głównego nurtu, który wydaje się dziwny. Jednak np. W Haskell jest to normalne. Na przykład może istnieć wiele różnych implementacji tej Ordklasy, a to, czy to, czy nie ajest mniejsza b, zależy od tego, która instancja klasy jest objęta zakresem.

Ale tak naprawdę wcale nie jest to takie dziwne! Zarówno Java (Comparator ), jak i .NET ( IComparer) mają interfejsy, które pozwalają wprowadzić własną relację porządkowania np. Do algorytmów sortowania. W ten sposób w pełni uznają, że uporządkowanie nie jest czymś, co jest ustalone na typ, ale zależy od kontekstu.

O ile mi wiadomo, obecnie nie ma języków, które wykonują takie tłumaczenie. Istnieje jednak pierwszeństwo: zarówno Ioke, jak i Seph mają to, co ich projektant nazywa „operatorami trójkowymi” - operatorami składniowo dwójkowymi, ale semantycznymi trzeciorzędnymi. W szczególności,

a = b

nie jest interpretowane jako wysłanie wiadomości =do aprzekazania bjako argument, ale raczej jako wysłanie wiadomości =do „aktualnego Ground” (koncepcja podobna, ale nie identyczna this) przekazanie ai bjako argument. Więc,a = b jest interpretowany jako

=(a, b)

i nie

a =(b)

Można to łatwo uogólnić na operatory n-ary.

Zauważ, że jest to naprawdę specyficzne dla języków OO. W OO zawsze mamy jeden obiekt, który jest ostatecznie odpowiedzialny za interpretację wysłanej wiadomości, i jak widzieliśmy, nie jest to od razu oczywiste dla czegoś takiego jaka < b < c który obiekt powinien być.

Nie dotyczy to jednak języków proceduralnych ani funkcjonalnych. Na przykład w Scheme , Common Lisp i Clojure The <funkcji n-ary i mogą być wywoływane z dowolnej liczby argumentów.

W szczególności, <czy nie oznacza „mniej niż”, a funkcje te są interpretowane nieco inaczej:

(<  a b c d) ; the sequence a, b, c, d is monotonically increasing
(>  a b c d) ; the sequence a, b, c, d is monotonically decreasing
(<= a b c d) ; the sequence a, b, c, d is monotonically non-decreasing
(>= a b c d) ; the sequence a, b, c, d is monotonically non-increasing
Jörg W Mittag
źródło
3

Po prostu dlatego, że projektanci języków o tym nie pomyśleli lub nie sądzili, że to dobry pomysł. Python robi to tak, jak to opisano za pomocą prostej (prawie) gramatyki LL (1).

Neil G.
źródło
1
To nadal będzie analizować normalną gramatykę w prawie dowolnym języku głównego nurtu; po prostu nie zostanie to poprawnie zrozumiane z powodu podanego przez @RobertHarvey.
Mason Wheeler
@MasonWheeler Nie, niekoniecznie. Jeśli reguły są napisane w taki sposób, że porównania są wymienne z innymi operatorami (powiedzmy, ponieważ mają one taki sam priorytet), to nie uzyskasz właściwego zachowania. Fakt, że Python umieszcza wszystkie porównania na jednym poziomie, pozwala mu traktować sekwencję jako koniunkcję.
Neil G
1
Nie całkiem. Wstaw 1 < 2 < 3do Java lub C # i nie masz problemu z pierwszeństwem operatora; masz problem z nieprawidłowymi typami. Problem polega na tym, że nadal będzie on analizowany dokładnie tak, jak go napisałeś, ale potrzebujesz logiki specjalnego przypadku w kompilatorze, aby przekształcić go z sekwencji pojedynczych porównań w porównanie łańcuchowe.
Mason Wheeler
2
@MasonWheeler Mówię, że język musi być zaprojektowany tak, aby działał. Jedną z nich jest poprawna gramatyka. (Jeśli reguły są napisane w taki sposób, że porównania są wymienne z innymi operatorami, powiedzmy, ponieważ mają one taki sam priorytet, to nie uzyskasz właściwego zachowania.) Kolejną częścią tego jest interpretowanie AST jako koniunkcji, którą C ++ nie robi Główną kwestią mojej odpowiedzi jest to, że jest to decyzja projektanta języka.
Neil G
@MasonWheeler Myślę, że się zgadzamy. Właśnie podkreślałem, że gramatyka nie jest trudna do tego. To tylko kwestia wcześniejszego podjęcia decyzji, że chcesz, aby działało w ten sposób.
Neil G
2

Poniższy program C ++ kompiluje się z nieznanym peep od clang, nawet z ostrzeżeniami ustawionymi na najwyższy możliwy poziom ( -Weverything):

#include <iostream>
int main () { std::cout << (1 < 3 < 2) << '\n'; }

Z drugiej strony pakiet kompilatora GNU ładnie mnie ostrzega comparisons like 'X<=Y<=Z' do not have their mathematical meaning [-Wparentheses].

Moje pytanie brzmi zatem : dlaczego x <y <z nie jest powszechnie dostępny w językach programowania z oczekiwaną semantyką?

Odpowiedź jest prosta: Kompatybilność wsteczna. Na wolności istnieje ogromna ilość kodu, który używa odpowiednika1<3<2 i oczekuje, że wynik będzie prawdziwy.

Projektant językowy ma tylko jedną szansę, aby uzyskać to „właściwe”, i właśnie w tym momencie język został zaprojektowany. Zrozumienie tego na początku oznacza, że ​​inni programiści raczej szybko skorzystają z tego „złego” zachowania. Poprawne ustawienie za drugim razem złamie istniejącą bazę kodu.

David Hammen
źródło
+1, ponieważ ten program wypisuje „1” jako wynik wyrażenia, które jest oczywiście fałszywe w matematyce. Chociaż jest wymyślony, prawdziwy przykład z niezrozumiałymi nazwami zmiennych stałby się koszmarem debugowania, gdyby dodano tę funkcję językową.
Seth Battin
@SethBattin - To nie jest koszmar debugowania w Pythonie. Jedynym problemem w Pythonie jest if x == y is True : ..., moim zdaniem: ludzie, którzy piszą tego rodzaju kodeks, zasługują na poddanie się specjalnym, nadzwyczajnym rodzajom tortur, które (gdyby żył teraz) spowodowałyby, że sam Torquemada zemdleje.
David Hammen