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) x
VB: CType(x, int32)
F #:x :> int32
Theoretisch könnte eine neue Sprache Operatoren für die meisten integrierten Funktionen enthalten. Anstelle von def
oder dec
oder var
für die Variablendeklaration, warum nicht ! name
oder @ name
ähnliches. Dies verkürzt mit Sicherheit die Deklaration, gefolgt von der Bindung: @x := 5
Statt declare x = 5
oder 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?
quelle
+
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.Antworten:
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
quelle
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 = 5
liest als"declare x equals 5"
oderlet x = 5
kann gelesen werden als"let x equal 5"
, was sehr verständlich ist, wenn es laut vorgelesen wird, jedoch@x := 5
wird 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.
quelle
@x := 5
ist nicht schwer herauszufinden - ich lese es mir immer noch vor alsset x to be equal to 5
.:=
eine breitere Verwendung in der mathematischen Notation außerhalb von Programmiersprachen. Auch Aussagen wiex = x + 1
werden ein bisschen absurd.:=
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.:=
in der Mathematik verwendet wurde. Die eine Definition, die ich dafür gefunden habe, war "definiert als", also@x := 5
wü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.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 * f
mit+(+(*(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).
quelle
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 bedeutetA * B
das? Es kommt ganz auf den Kontext an: Ist esA
ein 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 sindand
,or
,xor
undnot
, 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.quelle
and
und dieor
ganze 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.|
(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.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
foldRight
lesbarer als/:
.quelle
fold
oft 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.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 * c
einfacher zu schreiben und zu lesen alsC = 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.
quelle
Ich nehme an, das extremste Beispiel ist APL. Das folgende Programm ist das "Spiel des Lebens":
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.
quelle