Warum können nicht mehr Sprachen einen Wert mit mehr als einem anderen Wert vergleichen? [geschlossen]

10

Folgendes berücksichtigen:

if(a == b or c)

In den meisten Sprachen müsste dies wie folgt geschrieben werden:

if(a == b or a == c)

Das ist etwas umständlich und wiederholt Informationen.

Ich weiß, dass meine obige Beispielsyntax etwas klobig ist, aber ich bin sicher, dass es bessere Möglichkeiten gibt, die Idee zu vermitteln.

Warum bieten es nicht mehr Sprachen an? Gibt es Leistungs- oder Syntaxprobleme?

Zeroth
quelle
6
SQL bietet das: wo A IN (B, C)
Donnerstaggeek
4
Ich habe nicht nach Sprachen gefragt, die es anbieten oder haben können, aber warum bieten es nicht mehr Sprachen an? Gibt es Leistungs- oder Syntaxprobleme?
Zeroth
8
Um die Antwort von @ thursdaysgeek in den meisten Sprachen zu verallgemeinern, tun Sie dies normalerweise mit Set Containment. (Oder eine Liste oder ein Tupel, wenn das einfacher ist.) Es funktioniert genauso und vermeidet einige möglicherweise knifflige Syntaxprobleme. Bedeutet "b oder c" in Ihrem Beispiel die Menge "{b, c}" oder ist oder ein Operator wie || ? In Python bedeutet "b oder c" "den Wert von b, wenn wahr, oder den Wert von c"
Rob
4
Dies ist im Wesentlichen ein Syntaxproblem. Das vorliegende Problem besteht darin, den Unterschied zwischen "b oder c" und "b oder mit c" intuitiv zu unterscheiden.
YoungJohn
2
Es ist ziemlich hacky zu Sonderfällen a == b or c, und es ist meiner Meinung nach nicht einmal gut.

Antworten:

24

Das Syntaxproblem ist - dass es Syntax erfordert.

Unabhängig von der Syntax Ihrer Sprache müssen die Benutzer diese Sprache lernen. Andernfalls laufen sie Gefahr, Code zu sehen und nicht zu wissen, was er tut. Daher wird es im Allgemeinen als eine gute Sache angesehen, wenn eine Sprache eine einfache Syntax hat, die viele Fälle sauber behandelt.

In Ihrem speziellen Beispiel versuchen Sie, einen Infix-Operator (eine Funktion, die zwei Argumente akzeptiert, aber geschrieben ist Argument1 Operator Argument2) auf mehrere Argumente zu erweitern. Das funktioniert nicht sehr sauber, da der springende Punkt bei Infix-Operatoren, sofern es einen gibt, darin besteht, den Operator genau zwischen die beiden Argumente zu setzen. Die Erweiterung auf (Argument1 Operator Argument2 MagicallyClearSymbol Argument3...)scheint nicht viel Klarheit zu schaffen Equals(Arg1,Arg2,...). Infix wird normalerweise auch verwendet, um mathematische Konventionen zu emulieren, mit denen Menschen vertraut sind, was für eine alternative Syntax nicht zutreffen würde.

Mit Ihrer Idee wären keine besonderen Leistungsprobleme verbunden, außer dass der Parser sich mit einer Grammatik mit einer oder zwei anderen Produktionsregeln befassen müsste, was sich geringfügig auf die Geschwindigkeit des Parsens auswirken könnte. Dies kann für eine interpretierte oder JIT-kompilierte Sprache einen Unterschied machen, aber wahrscheinlich keinen großen Unterschied.

Das größere Problem mit der Idee ist nur, dass es eine schlechte Idee ist, viele Sonderfälle in einer Sprache zu erstellen .

psr
quelle
1
Nebenbei: Scala hat Infix-Operatoren mit einer beliebigen Anzahl von Argumenten, da Infix-Operatoren nur Methodenaufrufe ohne a sind .. Also würden sie geschrieben werden als arg1 op (arg2, arg3). Nicht gerade schön, aber an einigen Stellen im Kontext dieser Sprache benötigt.
Amon
was ist if my_var in (a, b)dann? Geht es nicht mehr darum, das richtige Werkzeug für den Job zu verwenden?
Tolle Punkte. Die Sprachsyntax sollte ein wesentlicher Bestandteil der Sprache sein, und dann bauen Sie darauf Bibliotheken auf. Wenn die Sprache mit "hilfreichem" syntaktischem Zucker zu voll ist, wird die Verwendung schwieriger. Nicht jeder braucht, a == b or cwährend andere wollen a == b or c but not d. IMO helfen hier Dienstprogrammfunktionen / Bibliotheken.
Allan
Möglicherweise ist ein Mittel erforderlich, mit dem eine Methode festlegen kann, dass ein Aufruf mit einer beliebigen Anzahl von Argumenten als mehrere Aufrufe behandelt werden soll, wobei die Ergebnisse auf irgendeine Weise kombiniert werden. Wenn f().Equals(a,b,c); könnte ausgewertet werden, da (var temp=f(); temp.Equals(a)||temp.Equals(b)||temp.Equals(c))diese Syntax perfekt wäre, aber wenn sie ausgewertet wird int[] arr = {a,b,c}; f().Equals(arr);, wäre das nicht so gut, insbesondere wenn für jeden Aufruf ein neues Array erstellt werden müsste.
Supercat
6

Weil es kein Problem ist und das Lösen im Grunde genommen keinen Nutzen bringt, bringt die Implementierung jedoch keine Kosten mit sich.

Bestehende bereichsbasierte Funktionen und solche, die praktisch jede Sprache bietet, können in dieser Situation perfekt funktionieren, wenn sie auf eine Größe skaliert werden, bei der a == b || a == csie nicht abgeschnitten werden.

DeadMG
quelle
2
+1, aber ich denke, die Antwort würde verbessert, wenn eine oder zwei dieser "vorhandenen bereichsbasierten Funktionen, die praktisch jede Sprache [anbietet]" gezeigt werden, nur damit diese Alternative klarer wird.
Avner Shahar-Kashtan
Können Sie beweisen, dass es "im Grunde genommen keinen Nutzen bringt, aber die Implementierung ungleich Null Kosten bringt"?
Darek Nędza
3
@ DarekNędza Die zweite Hälfte sollte nicht umstritten sein: Jede Funktion muss durchdacht, implementiert, getestet, dokumentiert und unterstützt werden. Keiner dieser Schritte ist unter einer angemessenen Metrik kostenlos (Zeit, Opportunitätskosten, Komplexität, Geldkosten, wenn jemand dafür bezahlt hat, usw.).
@ AvnerShahar-Kashtan Einverstanden - für mich ist es nicht offensichtlich, wie es beispielsweise in Java oder sh oder zsh aussehen würde? Ok, er hat vielleicht eine "moderne" Sprache impliziert. Groovy?
Volker Siegel
In PHP würde es so aussehen in_array($a, [$b, $c, $d, $e, $f]). : P
cHao
6

Einige Sprachen haben solche Funktionen. Zum Beispiel können wir in Perl6 Junctions verwenden , die „Überlagerungen“ zweier Werte sind:

if $a == any($b, $c) {
    say "yes";
}

# syntactic sugar for the above
if $a == $b | $c {
    say "yes";
}

Junctions ermöglichen es uns, Operationen an einem Datensatz ziemlich prägnant auszudrücken, ähnlich wie skalare Operationen in einigen Sprachen über Sammlungen verteilt werden. Wenn Sie beispielsweise Python mit numpy verwenden, kann der Vergleich auf alle Werte verteilt werden:

import numpy as np
2 == np.array([1, 2, 3])
#=> np.array([False, True, False], dtype=np.bool)
(2 == np.array([1, 2, 3])).any()
#=> True

Dies funktioniert jedoch nur für ausgewählte primitive Typen.

Warum sind Kreuzungen problematisch? Da Operationen an einer Junction über die enthaltenen Werte verteilt sind, verhält sich das Junction-Objekt selbst wie ein Proxy für Methodenaufrufe - etwas, das nur wenige Typsysteme außer der Ententypisierung verarbeiten können.

Typsystemprobleme können vermieden werden, wenn solche Junctions nur als spezielle Syntax für Vergleichsoperatoren zulässig sind . In diesem Fall sind sie jedoch so begrenzt, dass sie keinen ausreichenden Mehrwert bieten, um einer vernünftigen Sprache hinzugefügt zu werden. Das gleiche Verhalten könnte durch Festlegen von Operationen oder durch manuelles Schreiben aller Vergleiche ausgedrückt werden, und die meisten Sprachen glauben nicht daran, redundante Syntax hinzuzufügen, wenn es bereits eine perfekte Lösung gibt.

amon
quelle
Dieses spezielle numpy-Beispiel könnte klarer umgeschrieben werden als 2 in [1, 2, 3]. Auf der anderen Seite, wenn Numpy ein .all()oder etwas hat, ist die äquivalente einfache Python nicht annähernd so prägnant.
Izkata
@Izkata Ich habe speziell keine Set-Operationen verwendet. Während mein Beispiel den ==Operator verwendet hat, können wir <stattdessen auch verwenden - wo ist Ihr injetzt? Junctions sind allgemeiner als Set Mitgliedschaft Tests, weil Operationen an der Kreuzung alle Mitglieder verteilen über - (x|y).fooist x.foo|y.foo, bis zur Kreuzung schließlich auf einen einzigen Wert reduziert ist. Der bereitgestellte NumPy-Code zeigt eine genau äquivalente, aber ausführlichere Übersetzung der Perl6-Übergänge unter der Annahme primitiver Typen.
Amon
2

In Sprachen mit Makros ist es einfach, so etwas hinzuzufügen, wenn es nicht bereits vorhanden ist. Betrachten Sie Schläger

(define-syntax-rule (equal-any? a b ...)
  (or (equal? a b) ...))
(equal-any? "a" "b" "a")
> #t

In anderen Sprachen ohne Metaprogrammierung können Sie dies möglicherweise als Überprüfung der Set- / Listenmitgliedschaft umformulieren:

if a ∈ {b, c}
Phil
quelle
2
Die ersten beiden prüfen, ob alle Argumente gleich sind. OP möchte prüfen, ob das erste Argument einem der folgenden Argumente entspricht. Seltsamerweise respektiert das dritte Snippet, das Sie zeigen, dies.
@delnan Sorry, ich habe Sachen falsch verstanden. Ich habe es bearbeitet.
Phil
2

In einigen (populären) Sprachen ist der ==Operator nicht transitiv. Zum Beispiel ist in JavaScript 0gleich ''und '0', aber dann ''und '0'sind nicht gleich. Weitere solche Macken in PHP.

a == b == cDies bedeutet, dass eine weitere Mehrdeutigkeit hinzugefügt wird, da dies zu einem anderen Ergebnis führen kann, je nachdem, ob es als (a == b) & (a == c)oder interpretiert wird (a == b) & (a == c) & (b == c).

Konrad Morawski
quelle
2

In den meisten Sprachen sollte dies trivial durch Schreiben einer InFunktion erreichbar sein. Warum sollte sie also Teil der eigentlichen Sprache sein?

Linq zum Beispiel hat Contains().

Okay, für alle Pedanten ist hier meine Implementierung in C #:

public static bool In<T>(this T obj, params T[] values)
{
    for(int i=0; i < values.Length; i++)
    {
        if (object.Equals(obj, values[i]))
            return true;
    }
    return false;
}
Robert Harvey
quelle
Dies funktioniert in einem Laufzeitbereich von Werten, nicht in einem Tupel, wie der OP-Code ausgedrückt werden könnte.
DeadMG
Nur weil es einfach ist, heißt das nicht, dass es nicht getan werden sollte. Es ist ... Bau in Betracht ziehen. Warum müssen wir all diese grundlegenden Funktionen und Algorithmen immer und immer und immer wieder manuell schreiben?
Zeroth
5
@Zeroth Vielleicht schreiben Sie immer wieder dasselbe, aber andere verwenden stattdessen eher die Abstraktionsmechanismen ihrer Sprache. Wenn Sie sich selbst zu schreiben sehen a == b || a == cmehrere Male, vielleicht ist es Zeit fürequals_any(a, {b, c})
amon
Eine "enthält" -Implementierung lässt sich nicht einfach auf Dinge wie if (a > (b or c))und erweitern if (a mod (b or c) == 2).
tobyink
1
Hat jemand Pedanten gesagt? :) Es ist eine foreach-Schleife, also gibt es keine iVariable. Und insgesamt scheint es geschrieben zu sein, nachdem Sie einen langen Tag hinter sich haben :) Weil das Einfügen von beiden return trueund return falsein die Schleife hier bedeutet, dass es auf keinen Fall über die erste Iteration hinausgeht. Sie vergleichen nur mit dem ersten value. Übrigens, warum nicht Anywie von @Bob vorgeschlagen verwenden und vereinfachen inreturn values.Any(value => Object.Equals(obj, value));
Konrad Morawski
1

"if (a == b oder c)" funktioniert in den meisten Sprachen: wenn a == b oder wenn c nicht negativ, null oder null ist.

Sich zu beschweren, dass es ausführlich ist, geht am Punkt vorbei: Sie sollten nicht ein Dutzend Dinge in eine Bedingung stapeln. Wenn Sie einen Wert mit einer beliebigen Anzahl anderer Werte vergleichen müssen, erstellen Sie eine Unterroutine.

Satanicpuppy
quelle
3
Welche Sprachen machen "am meisten" aus?
FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner, na ja, wenn csich ein Boolescher Wert ergibt, kann so ziemlich jede Sprache damit umgehen a == b || c:)
Brian S
@BrianS: Ich nahm an, dass OP die wörtliche Syntax bedeutet if(a == b or c). Ich muss eine Pause machen, denke ich ...: P
FrustratedWithFormsDesigner
@FrustratedWithFormsDesigner Lisp! ... huh? ... :)
Volker Siegel
3
Dies verfehlt wirklich den Punkt der Frage. if (a == b or c)ist ein Pseudocode, um zu prüfen, ob er agleich boder agleich ist c. Es ist nicht dazu gedacht zu überprüfen, ob ces nicht Null ist.
HDD
1

Normalerweise möchten Sie Ihre Syntax auf ein Minimum beschränken und stattdessen zulassen, dass solche Konstrukte in der Sprache selbst definiert werden.

In Haskell können Sie beispielsweise jede Funktion mit zwei oder mehr Argumenten mithilfe von Backticks in einen Infix-Operator konvertieren. So können Sie schreiben:

if a `elem` [b, c] then ... else ...

Dabei elemhandelt es sich nur um eine normale Funktion, die zwei Argumente verwendet - einen Wert und eine Liste von Werten - und prüft, ob das erste ein Element des zweiten ist.

Was ist, wenn Sie andanstelle von verwenden möchten or? In Haskell können Sie einfach Folgendes verwenden, anstatt darauf zu warten, dass der Compilerhersteller eine neue Funktion implementiert:

 if all (== a) [b, c] then ... else ...
Tobias Brandt
quelle
1
Warum sollte man die Syntax auf ein Minimum beschränken wollen? Was genau ist der Kompromiss dort? Machen Sie solche Proklamationen nicht ohne Argumente. ;)
Zeroth
1

Einige Sprachen bieten dies an - bis zu einem gewissen Grad.

Vielleicht nicht als Ihr spezifisches Beispiel, aber nehmen Sie zum Beispiel eine Python-Zeile:

def minmax(min, max):
    def answer(value):
        return max > value > min
    return answer

inbounds = minmax(5, 15)
inbounds(7) ##returns True
inbounds(3) ##returns False
inbounds(18) ##returns False

Einige Sprachen eignen sich also gut für mehrere Vergleiche, solange Sie es richtig ausdrücken.

Leider funktioniert es nicht so, wie Sie es für Vergleiche erwarten würden.

>>> def foo(a, b):
...     def answer(value):
...         return value == a or b
...     return answer
... 
>>> tester = foo(2, 4)
>>> tester(3)
4
>>> tester(2)
True
>>> tester(4)
4
>>> 

"Was meinst du damit, dass es entweder True oder 4 zurückgibt?" - die Miete nach Ihnen

Eine Lösung in diesem Fall besteht zumindest bei Python darin, es etwas anders zu verwenden:

>>> def bar(a, b):
...     def ans(val):
...             return val == a or val == b
...     return ans
... 
>>> this = bar(4, 10)
>>> this(5)
False
>>> this(4)
True
>>> this(10)
True
>>> this(9)
False
>>> 

EDIT: Das Folgende würde auch etwas Ähnliches tun, wieder in Python ...

>>> def bar(a, b):
...     def answer(val):
...             return val in (a, b)
...     return answer
... 
>>> this = bar(3, 5)
>>> this(3)
True
>>> this(4)
False
>>> this(5)
True
>>> 

Unabhängig davon, welche Sprache Sie verwenden, kann es sein, dass Sie dies nicht können, sondern dass Sie sich zunächst genauer ansehen müssen, wie die Logik tatsächlich funktioniert. Normalerweise geht es nur darum zu wissen, was Sie tatsächlich von der Sprache verlangen, um es Ihnen zu sagen.


quelle
1

Die indexOf-Methode, die in einem Array verwendet wird und in fast allen Sprachen verfügbar ist, ermöglicht es, einen Wert mit mehreren anderen zu vergleichen. Ich denke, ein spezieller Operator macht nicht viel Sinn.

In Javascript würde das schreiben:

if ( [b, c].indexOf(a) != -1 ) { ....  }
GameAlchemist
quelle
0

Sie fragen, warum können wir das nicht tun: if(a == b or c)

Python tut dies sehr effizient in der Tat, am effizientesten mit set:

if a in set([b, c]):
    then_do_this()

Beim Testen der Mitgliedschaft prüft 'set', ob die Hashes des Elements identisch sind, und vergleicht erst dann die Gleichheit. Daher müssen die Elemente b und c hashbar sein. Andernfalls wird eine Liste direkt mit der Gleichheit verglichen:

if a in [b, c]:
    then_do_this()
Aaron Hall
quelle
0

Mit Sprachen im APL-Stil können Sie einen Skalar mit jedem Element in einem Vektor in einer einzigen Operation vergleichen. Dies erzeugt einen Booleschen Vektor. Zum Beispiel möchte ich schamlos für meinen minimal ausgestatteten apl-Rechner inca ( Online-Dolmetscher ) werben .

   a<5
5 
   b<4
4 
   c<5
5 
   a=b c
0 1 

Um dies auf einen einzelnen Wert zu reduzieren, können wir ein Inklusivwert oder eine Summierung und Überprüfung auf einen Wert ungleich Null durchführen.

   0!+/a=b c
1 
   c<6
6 
   0!+/a=b c
0

Wie die anderen Antworten sagen, ist das Problem die Syntax. Zu einem gewissen Grad Syntax Lösungen werden gefunden, an den vielleicht hohen Kosten des Arrays Paradigma zu lernen.

luser droog
quelle