Sind Operatoren übersichtlicher als Schlüsselwörter oder Funktionen? [geschlossen]

14

Es ist ein bisschen subjektiv, aber ich hoffe, dass ich besser verstehe, welche Faktoren einen Bediener dazu bringen, die Verwendung klarer als stumpf und schwierig zu machen. Ich habe in letzter Zeit über Sprachentwürfe nachgedacht, und ein Problem, bei dem ich immer wieder zurückkehre, ist, wann man eine Schlüsseloperation in der Sprache zum Operator macht und wann man ein Schlüsselwort oder eine Funktion verwendet.

Haskell ist dafür etwas berüchtigt, da benutzerdefinierte Operatoren einfach zu erstellen sind und häufig ein neuer Datentyp mit mehreren Operatoren zur Verwendung darauf gepackt wird. Die Parsec-Bibliothek enthält zum Beispiel eine Menge Operatoren zum Kombinieren von Parsern mit Edelsteinen wie >.und .> ich kann mich nicht einmal daran erinnern, was sie jetzt bedeuten, aber ich erinnere mich, dass es sehr einfach ist, mit ihnen zu arbeiten, wenn ich mir was auswendig gelernt habe sie meinen eigentlich. Wäre ein Funktionsaufruf wie leftCompose(parser1, parser2)besser gewesen? Sicher ausführlicher, aber in gewisser Weise klarer.

Überladungen von Operatoren in C-ähnlichen Sprachen sind ein ähnliches Problem, das jedoch durch das zusätzliche Problem der Überladung der Bedeutung vertrauter Operatoren +mit ungewöhnlichen neuen Bedeutungen verursacht wird.

In jeder neuen Sprache scheint dies ein ziemlich schwieriges Thema zu sein. In F # verwendet das Casting beispielsweise einen mathematisch abgeleiteten Typ-Casting-Operator anstelle der Cast-Syntax im C # -Stil oder des ausführlichen VB-Stils. C #: (int32) xVB: CType(x, int32)F #:x :> int32

Theoretisch könnte eine neue Sprache Operatoren für die meisten integrierten Funktionen enthalten. Anstelle von defoder decoder varfür die Variablendeklaration, warum nicht ! nameoder @ nameähnliches. Dies verkürzt mit Sicherheit die Deklaration, gefolgt von der Bindung: @x := 5Statt declare x = 5oder Für die let x = 5 meisten Codes sind viele Variablendefinitionen erforderlich. Warum also nicht?

Wann ist ein Operator klar und nützlich und wann ist er undeutlich?

CodexArcanum
quelle
1
Ich wünsche mir, dass einer der 1.000 Entwickler, die sich jemals über Javas mangelnde Operatorüberladung bei mir beschwert haben, diese Frage beantworten würde.
smp7d
1
Ich hatte an APL gedacht, als ich die Frage abgetippt hatte, aber auf eine Weise, die den Punkt klarer macht und sie in ein etwas weniger subjektives Gebiet verschiebt. Ich denke, die meisten Leute könnten zustimmen, dass APL durch die extreme Anzahl von Operatoren an Benutzerfreundlichkeit einiges einbüßt, aber die Anzahl der Leute, die sich beschweren, wenn eine Sprache nicht überladen ist, spricht sicherlich für einige Funktionen, für die sie gut geeignet sind agieren als Betreiber. Zwischen verbotenen benutzerdefinierten Operatoren und nichts als Operatoren müssen einige Richtlinien für die praktische Implementierung und Verwendung verborgen sein.
CodexArcanum
Aus meiner Sicht ist eine mangelnde Überladung von Operatoren zu beanstanden, da sie native Typen gegenüber benutzerdefinierten Typen privilegiert (beachten Sie, dass Java dies auch auf andere Weise tut). Ich bin viel mehr ambivalent über Haskell-Stil benutzerdefinierte Operatoren, die zu Schwierigkeiten wie eine offene Einladung scheinen ...
comingstorm
2
@comingstorm: Ich denke eigentlich, der Haskell-Weg ist besser. Wenn Sie über eine begrenzte Anzahl von Operatoren verfügen, die Sie auf unterschiedliche Weise überladen können, müssen Sie Operatoren häufig in unterschiedlichen Kontexten wiederverwenden (z. B. +für die Verkettung von Zeichenfolgen oder <<für Streams). In Haskell hingegen ist ein Operator entweder nur eine Funktion mit einem benutzerdefinierten Namen (dh ohne Überladung) oder Teil einer Typklasse. Dies bedeutet, dass der Operator zwar polymorph ist, aber für jeden Typ dieselbe logische Aktion ausführt hat sogar die gleiche Typunterschrift. Also >>ist >>für jeden Typ etwas dabei und wird auch nie ein bisschen verschoben.
Tikhon Jelvis

Antworten:

16

Unter dem Gesichtspunkt der allgemeinen Sprachgestaltung muss es keinen Unterschied zwischen Funktionen und Operatoren geben. Man könnte Funktionen als Präfixoperationen mit einer beliebigen (oder sogar variablen) Arität beschreiben. Und Schlüsselwörter können nur als Funktionen oder Operatoren mit reservierten Namen betrachtet werden (was beim Entwerfen einer Grammatik hilfreich ist).

Bei all diesen Entscheidungen kommt es letztendlich darauf an, wie die Notation gelesen werden soll, und das ist, wie Sie sagen, subjektiv, obwohl man einige offensichtliche Rationalisierungen vornehmen kann, z. B. die üblichen Infixoperatoren für Mathematik verwenden, wie sie jeder kennt

Zuletzt schrieb Dijkstra eine interessante Begründung der von ihm verwendeten mathematischen Notationen , die eine gute Diskussion der Kompromisse zwischen Infix- und Präfixnotationen einschließt

jk.
quelle
4
Ich würde Ihnen gerne eine zweite +1 für den Link zu Dijkstra Papier geben.
Programmierer
Toller Link, man musste ein PayPal-Konto einrichten! ;)
Adriano Repetti
8

Für mich ist ein Operator nicht mehr nützlich, wenn Sie die Codezeile nicht mehr laut oder im Kopf lesen können und es sinnvoll finden.

Zum Beispiel declare x = 5liest als "declare x equals 5"oder let x = 5kann gelesen werden als "let x equal 5", was sehr verständlich ist, wenn es laut vorgelesen wird, jedoch @x := 5wird gelesen als "at x colon equals 5"(oder "at x is defined to be 5"wenn Sie Mathematiker sind), was überhaupt keinen Sinn ergibt.

Verwenden Sie daher meiner Meinung nach einen Operator, wenn der Code von einer einigermaßen intelligenten Person, die mit der Sprache nicht vertraut ist, laut vorgelesen und verstanden werden kann, und verwenden Sie andernfalls Funktionsnamen.

Rachel
quelle
5
Ich denke, es @x := 5ist nicht schwer herauszufinden - ich lese es mir immer noch vor als set x to be equal to 5.
FrustratedWithFormsDesigner
3
@Rachel hat in diesem Fall :=eine breitere Verwendung in der mathematischen Notation außerhalb von Programmiersprachen. Auch Aussagen wie x = x + 1werden ein bisschen absurd.
Ccoakley
3
Ironischerweise hatte ich vergessen, dass manche Leute nicht :=als Aufgabe erkennen würden . Einige Sprachen verwenden das tatsächlich. Smalltalk, denke ich, ist vielleicht der bekannteste. Ob Zuweisung und Gleichheit getrennte Operatoren sein sollten, ist eine ganz andere Dose von Würmern, die wahrscheinlich bereits von einer anderen Frage abgedeckt wird.
CodexArcanum
1
@ccoakley Danke, mir war nicht bewusst, dass :=in der Mathematik verwendet wurde. Die eine Definition, die ich dafür gefunden habe, war "definiert als", also @x := 5würde sie ins Englische übersetzt "at x is defined to be 5", was für mich immer noch nicht so viel Sinn ergibt, wie es sollte.
Rachel
1
Natürlich verwendete Pascal: = als Zuweisung, da in ASCII kein Pfeil nach links angezeigt wird.
Vatine
4

Ein Bediener ist klar und nützlich, wenn er vertraut ist. Und das bedeutet wahrscheinlich, dass das Überladen von Operatoren nur durchgeführt werden sollte, wenn der gewählte Operator (wie in Form, Rangfolge und Assoziativität) als bereits etablierte Praxis nahe genug ist.

Aber ein bisschen weiter graben, gibt es zwei Aspekte.

Die Syntax (Infix für Operator im Gegensatz zum Präfix für Funktionsaufrufsyntax) und die Benennung.

Für die Syntax gibt es einen anderen Fall, in dem Infixe klarer als Präfixe sind, so dass es die Anstrengung rechtfertigt, sich vertraut zu machen: wenn Aufrufe verkettet und verschachtelt sind.

Vergleichen Sie a * b + c * d + e * fmit +(+(*(a, b), *(c, d)), *(e, f))oder + + * a b * c d * e f(beide können im selben Zustand wie die Infix-Version analysiert werden). Die Tatsache, dass die +Begriffe durch das Trennen voneinander getrennt werden, anstatt einem von ihnen weit vorauszugehen, macht sie in meinen Augen besser lesbar (aber Sie müssen sich an Vorrangregeln erinnern, die für die Präfixsyntax nicht benötigt werden). Wenn Sie Dinge auf diese Weise zusammensetzen müssen, ist der langfristige Nutzen die Lernkosten wert. Und Sie behalten diesen Vorteil, auch wenn Sie Ihre Operatoren mit Buchstaben anstelle von Symbolen benennen.

In Bezug auf die Benennung von Operatoren sehe ich nur wenige Vorteile darin, etwas anderes als festgelegte Symbole oder eindeutige Namen zu verwenden. Ja, sie mögen kürzer sein, aber sie sind wirklich kryptisch und werden schnell vergessen, wenn Sie keinen guten Grund haben, sie bereits zu kennen.

Ein dritter Aspekt, wenn Sie ein Sprachdesign-POV wählen, ist, ob die Gruppe von Operatoren offen oder geschlossen sein soll - können Sie einen Operator hinzufügen oder nicht - und ob die Priorität und Assoziativität benutzerspezifizierbar sein sollen oder nicht. Meine erste Einstellung wäre, vorsichtig zu sein und keine benutzerspezifizierbare Priorität und Assoziativität bereitzustellen, während ich offener wäre, wenn ich einen offenen Satz hätte (dies würde den Druck erhöhen, das Überladen von Operatoren zu verwenden, aber den Druck verringern, einen schlechten zu verwenden nur um von der infix-syntax zu profitieren (wo es nützlich ist).

Ein Programmierer
quelle
Wenn Benutzer Operatoren hinzufügen können, lassen Sie sie dann die Priorität und Assoziativität der von ihnen hinzugefügten Operatoren festlegen.
Tikhon Jelvis,
@TikhonJelvis, Es war größtenteils konservativ, aber es gibt einige Aspekte zu berücksichtigen, wenn Sie es für etwas anderes als Beispiele verwenden möchten. Beispielsweise möchten Sie die Priorität und die Assoziativität an den Operator binden, nicht an ein bestimmtes Mitglied der überladenen Menge (Sie haben das Mitglied nach Abschluss des Parsings ausgewählt, die Auswahl kann das Parsing nicht beeinflussen). Um Bibliotheken mit demselben Operator zu integrieren, müssen sie sich daher auf ihre Priorität und Assoziativität einigen. Bevor ich die Möglichkeit hinzufüge, würde ich versuchen herauszufinden, warum so wenige Sprachen Algol 68 folgten (AFAIK none) und sie definieren durften.
Programmierer
In Haskell können Sie beliebige Operatoren definieren und deren Priorität und Assoziativität festlegen. Der Unterschied besteht darin, dass Sie Operatoren nicht per se überladen können - sie verhalten sich wie normale Funktionen. Das heißt, Sie können nicht verschiedene Bibliotheken haben, die versuchen, denselben Operator für verschiedene Dinge zu verwenden. Da Sie Ihren eigenen Operator definieren können, gibt es weitaus weniger Gründe, dieselben für verschiedene Zwecke wiederzuverwenden.
Tichon Jelvis
1

Operatoren als Symbole sind nützlich, wenn sie intuitiv sinnvoll sind. +und -sind offensichtlich Addition und Subtraktion, weshalb so ziemlich jede Sprache sie jemals als ihre Operatoren verwendet. Dasselbe gilt für Vergleiche ( <und >werden in der Grundschule unterrichtet <=und >=sind intuitive Erweiterungen der grundlegenden Vergleiche, wenn Sie verstehen, dass die Standardtastatur keine unterstrichenen Vergleichstasten enthält.)

*und /sind weniger unmittelbar ersichtlich, werden jedoch allgemein verwendet, und ihre Position auf dem Ziffernblock direkt neben den Tasten +und -hilft dabei, den Kontext bereitzustellen. C <<und >>sind in der gleichen Kategorie: Wenn Sie verstehen, was eine Linksverschiebung und eine Rechtsverschiebung sind, ist es nicht allzu schwierig, die Mnemonik hinter den Pfeilen zu verstehen.

Dann kommen Sie zu einigen der wirklich seltsamen Dinge, die C-Code in eine unlesbare Operator-Suppe verwandeln können. (Wählen Sie hier C, weil es ein sehr operatorlastiges Beispiel ist, dessen Syntax so gut wie jeder kennt, entweder durch die Arbeit mit C oder mit untergeordneten Sprachen.) Zum Beispiel dann %operator. Jeder weiß, was das ist: Es ist ein Prozentzeichen ... richtig? Mit Ausnahme von C hat dies nichts mit der Division durch 100 zu tun. Es ist der Modul Operator. Das macht mnemonisch keinen Sinn, aber da ist es!

Noch schlimmer sind die Booleschen Operatoren. &als und ist sinnvoll, außer dass es zwei verschiedene &Operatoren gibt, die zwei verschiedene Dinge tun, und beide sind in jedem Fall syntaktisch gültig, in dem einer von ihnen gültig ist, auch wenn in einem bestimmten Fall immer nur einer von ihnen tatsächlich Sinn ergibt. Das ist nur ein Rezept für Verwirrung! Die Operatoren or , xor und not sind noch schlimmer, da sie keine mnemonisch nützlichen Symbole verwenden und auch nicht konsistent sind. (Kein Doppel xor Operator und die logische und bitweise nicht zwei verschiedene Symbole verwenden , anstatt ein Symbol und eine verdoppelte-up - Version.)

Und nur um es noch schlimmer, die &und *erhalten Betreiber für ganz andere Dinge wiederverwendet, die die Sprache härter macht für Menschen und Compiler zu analysieren. (Was bedeutet A * Bdas? Es kommt ganz auf den Kontext an: Ist es Aein Typ oder eine Variable?)

Ich habe zwar nie mit Dennis Ritchie gesprochen und ihn nach den Designentscheidungen gefragt, die er in der Sprache getroffen hat, aber es scheint, dass die Philosophie hinter den Operatoren "Symbole zum Wohle der Symbole" war.

Vergleichen Sie dies mit Pascal, der dieselben Operatoren wie C hat, diese jedoch anders darstellt: Operatoren sollten intuitiv und leicht lesbar sein. Die arithmetischen Operatoren sind die gleichen. Der Moduloperator ist das Wort mod, da auf der Tastatur kein Symbol steht, das offensichtlich "Modul" bedeutet. Die logischen Operatoren sind and, or, xorund not, und es gibt nur eins von jedem; Der Compiler weiß, ob Sie die Boolesche oder die bitweise Version benötigen, je nachdem, ob Sie mit Booleschen Werten oder mit Zahlen arbeiten, wodurch eine ganze Klasse von Fehlern beseitigt wird. Dadurch ist Pascal-Code viel einfacher zu verstehen als C-Code, insbesondere in einem modernen Editor mit Syntaxhervorhebung, sodass sich die Operatoren und Schlüsselwörter visuell von den Bezeichnern unterscheiden.

Mason Wheeler
quelle
4
Pascal vertritt überhaupt keine andere Philosophie. Wenn Sie Wirths Zeitungen lesen, verwendet er Symbole für Dinge wie andund die organze Zeit. Als er Pascal entwickelte, tat er dies jedoch auf einem Control Data-Mainframe, der 6-Bit-Zeichen verwendete. Dass die Entscheidung zu verwenden Worte für viele Dinge, aus dem einfachen Grund gezwungen , dass der Zeichensatz einfach nicht haben fast so viel Platz für Symbole und wie so etwas wie ASCII. Ich habe sowohl C als auch Pascal verwendet und finde C wesentlich lesbarer. Sie mögen Pascal bevorzugen, aber das ist Geschmackssache, keine Tatsache.
Jerry Coffin
Um die Bemerkung von @JerryCoffin über Wirth zu ergänzen, werden in den folgenden Sprachen (Modula, Oberon) mehr Symbole verwendet.
Programmierer
@AProgrammer: Und sie sind nie irgendwo hingegangen. ;)
Mason Wheeler
Ich denke |(und im weiteren ||Sinne) für oder macht Sinn. Sie sehen es auch immer wieder als Abwechslung in anderen Kontexten, wie z. B. Grammatik, und es sieht so aus, als würde es zwei Optionen aufteilen.
Tikhon Jelvis
1
Ein Vorteil von buchstabenbasierten Schlüsselwörtern besteht darin, dass ihr Platz viel größer ist als der von Symbolen. Eine Sprache im Pascal-Stil kann beispielsweise Operatoren für "Modul" und "Rest" sowie für die Division zwischen Ganzzahl und Gleitkomma enthalten (die über die Typen ihrer Operanden hinaus unterschiedliche Bedeutungen haben).
Supercat
1

Ich denke, Operatoren sind viel besser lesbar, wenn ihre Funktion ihren bekannten mathematischen Zweck genau widerspiegelt. Zum Beispiel sind Standardoperatoren wie +, - usw. besser lesbar als Addieren oder Subtrahieren, wenn Addition und Subtraktion ausgeführt werden. Das Verhalten des Bedieners muss klar definiert sein. Ich kann zum Beispiel eine Überladung der Bedeutung von + für die Listenverkettung tolerieren. Im Idealfall würde die Operation keine Nebenwirkungen haben und einen Wert zurückgeben, anstatt zu mutieren.

Ich habe allerdings mit Verknüpfungsoperatoren für Funktionen wie Fold zu kämpfen. Offensichtlich würde mehr Erfahrung mit ihnen das erleichtern, aber ich finde foldRightlesbarer als /:.

Matt H
quelle
4
Vielleicht kommt auch die Häufigkeit der Nutzung hinzu. Ich würde nicht denken, dass Sie foldoft genug, dass ein Operator eine ganze Menge Zeit spart. Das könnte auch der Grund sein, warum LISPy (+ 1 2 3) seltsam erscheint, aber (addiere 1 2 3) weniger. Wir neigen dazu, Operatoren als rein binär zu betrachten. Die >>.Style-Operatoren von Parsec mögen doof sein, aber das Wichtigste, was Sie in Parsec tun, ist das Kombinieren von Parsern, sodass Operatoren dafür viel Verwendung finden.
CodexArcanum
1

Das Problem mit Operatoren ist, dass es nur eine kleine Anzahl von ihnen gibt, verglichen mit der Anzahl der sinnvollen (!) Methodennamen, die stattdessen verwendet werden könnten. Ein Nebeneffekt ist, dass benutzerdefinierte Operatoren dazu neigen, eine Menge Überladung einzuführen.

Natürlich ist die Zeile C = A * x + b * ceinfacher zu schreiben und zu lesen als C = A.multiplyVector(x).addVector(b.multiplyScalar(c)).

Oder es ist sicherlich einfacher zu schreiben, wenn Sie alle überladenen Versionen all dieser Operatoren im Kopf haben. Und Sie können es später lesen, während Sie den vorletzten Fehler beheben.

Nun, für die meisten Codes, die dort herauskommen werden, ist das in Ordnung. "Das Projekt besteht alle Tests und läuft" - für so manche Software ist das alles, was wir jemals wollen könnten.

Bei Software, die in kritische Bereiche vordringt, sieht die Sache jedoch anders aus. Sicherheitskritisches Zeug. Sicherheit. Software, die Flugzeuge in der Luft hält oder kerntechnische Anlagen am Laufen hält. Oder Software, die Ihre streng vertraulichen E-Mails verschlüsselt.

Für Sicherheitsexperten, die auf die Prüfung von Code spezialisiert sind, kann dies ein Albtraum sein. Die Mischung aus einer Fülle von benutzerdefinierten Operatoren und einer Überlastung der Operatoren kann sie in die unangenehme Situation bringen, dass sie Schwierigkeiten haben, herauszufinden, welcher Code letztendlich ausgeführt wird.

Dies ist also, wie in der ursprünglichen Frage ausgeführt, ein höchst subjektives Thema. Und während so mancher Vorschlag zur Verwendung von Operatoren durchaus sinnvoll klingt, kann die Kombination von nur wenigen auf lange Sicht viel Ärger bereiten.

Doppelfisch
quelle
0

Ich nehme an, das extremste Beispiel ist APL. Das folgende Programm ist das "Spiel des Lebens":

life←{↑1 ⍵∨.∧3 4=+/,¯1 0 1∘.⊖¯1 0 1∘.⌽⊂⍵}

Und wenn Sie herausfinden können, was das alles bedeutet, ohne ein paar Tage mit dem Referenzhandbuch zu verbringen, dann wünschen wir Ihnen viel Glück!

Das Problem ist, dass Sie eine Reihe von Symbolen haben, die fast jeder intuitiv versteht: +,-,*,/,%,=,==,&

und der Rest, der einer Erklärung bedarf, bevor er verstanden werden kann, ist im Allgemeinen spezifisch für die jeweilige Sprache. Beispielsweise gibt es kein offensichtliches Symbol, um anzugeben, welches Mitglied eines Arrays Sie möchten, [], () und sogar "." verwendet wurden, aber es gibt auch kein naheliegendes Schlüsselwort, das Sie leicht verwenden könnten, so dass ein Fall für einen umsichtigen Umgang mit Operatoren vorliegt. Aber nicht zu viele von ihnen.

James Anderson
quelle