Warum gibt es so wenige Sprachen mit einem Operator vom Variablentyp?

47

Ich meine es so:

<?php
    $number1 = 5;   // (Type 'Int')
    $operator1 = +; // (Type non-existent 'Operator')
    $number2 = 5;   // (Type 'Int')
    $operator2 = *; // (Type non-existent 'Operator')
    $number3 = 8;   // (Type 'Int')

    $test = $number1 $operator1 $number2 $operator2 $number3; //5 + 5 * 8.

    var_dump($test);
?>

Aber auch so:

<?php
    $number1 = 5;
    $number3 = 9;
    $operator1 = <;

    if ($number1 $operator1 $number3) { //5 < 9 (true)
        echo 'true';
    }
?>

Es scheint, als hätten keine Sprachen dies - gibt es einen guten Grund, warum sie das nicht tun?

kgongonowdoe
quelle
28
Im Allgemeinen wird das, was Sie tun möchten, von allen Sprachen abgedeckt, die irgendeine Form der Metaprogrammierung mit irgendwelchen Lambdas, Abschlüssen oder anonymen Funktionen unterstützen, die der übliche Weg sind, solche Funktionen zu implementieren. Mit Sprachen, in denen Methoden erstklassige Bürger sind, können Sie sie mehr oder weniger identisch mit Variablen verwenden. Obwohl nicht in genau dieser einfachen Syntax, die Sie hier verwenden, da in den meisten solchen Sprachen klargestellt werden muss, dass Sie die in der Variablen gespeicherte Methode tatsächlich aufrufen möchten.
Thorsten Müller
7
@MartinMaat Funktionale Sprachen machen das oft.
Thorbjørn Ravn Andersen
7
in haskell sind operator funktionen wie jede andere funktion. der Typ (+)ist Num a => a -> a -> aIIRC. Sie können auch Funktionen definieren, damit sie ( a + banstelle von (+) a b) als Infix geschrieben werden können
sara
5
@enderland: Deine Bearbeitung hat das Ziel der Frage komplett verändert. Es ging von der Frage, ob irgendwelche Sprachen existieren, zu der Frage, warum so wenige existieren. Ich denke, Ihre Bearbeitung wird zu vielen verwirrten Lesern führen.
Bryan Oakley
5
@enderland: Während das stimmt, dient ein vollständiger Wechsel des Themas nur zur Verwirrung. Wenn es kein Thema ist, stimmt die Community ab, um es zu schließen. Keine der Antworten (zum Zeitpunkt, an dem ich dies schreibe) ergibt für die Frage, wie sie geschrieben ist, einen Sinn.
Bryan Oakley

Antworten:

104

Operatoren sind nur Funktionen unter lustigen Namen mit einer speziellen Syntax.

In vielen Sprachen wie C ++ und Python können Sie Operatoren neu definieren, indem Sie spezielle Methoden Ihrer Klasse überschreiben. Dann arbeiten Standardoperatoren (z. B. +) nach der von Ihnen angegebenen Logik (z. B. Verketten von Zeichenfolgen oder Hinzufügen von Matrizen oder was auch immer).

Da solche operatordefinierenden Funktionen nur Methoden sind, können Sie sie wie eine Funktion weitergeben:

# python
action = int.__add__
result = action(3, 5)
assert result == 8

In anderen Sprachen können Sie neue Operatoren direkt als Funktionen definieren und als Infix verwenden.

-- haskell
plus a b = a + b  -- a normal function
3 `plus` 5 == 8 -- True

(+++) a b = a + b  -- a funny name made of non-letters
3 +++ 5 == 8 -- True

let action = (+)
1 `action` 3 == 4 -- True

Leider bin ich mir nicht sicher, ob PHP so etwas unterstützt und ob es eine gute Sache wäre, es zu unterstützen. Verwenden Sie eine einfache Funktion, die besser lesbar ist als $foo $operator $bar.

9000
quelle
2
@tac: Ja, das ist nett und kann sogar irgendwie in andere Sprachen portiert werden :) In Bezug auf Haskell fehlt mir am meisten der $Operator, der Klammern vermeidet (insbesondere mehrere verschachtelte) - aber das kann nur mit non funktionieren -variadic Funktionen, also ohne zB Python und Java. Die Zusammensetzung der unären OTOH-Funktionen ist gut möglich .
9000
6
Ich war noch nie ein Fan von Überladungen durch Operatoren, denn ja, ein Operator ist nur eine Funktion mit spezieller Syntax, aber es gibt einen impliziten Vertrag, der normalerweise für Operatoren gilt, die nicht für Funktionen gelten. "+" hat beispielsweise bestimmte Erwartungen - Vorrang von Operatoren, Kommutativität usw. - und gegen diese Erwartungen zu verstoßen, ist ein sicherer Weg, um Menschen zu verwirren und Fehler zu generieren. Ein Grund, warum ich, obwohl ich Javascript liebe, es vorgezogen hätte, zwischen + für das Hinzufügen und Verketten zu unterscheiden. Perl hatte es genau dort.
fool4jesus
4
Beachten Sie, dass Python über ein Standardmodul verfügt operator, mit dem Sie schreiben können action = operator.add, und dass es für jeden Typ funktioniert, der definiert +(nicht nur int).
Dan04
3
In Haskell wird +die Num-Typenklasse bearbeitet, sodass Sie sie +für jeden neu erstellten Datentyp implementieren können , aber auf die gleiche Weise, wie Sie alles andere tun, z. B. fmap für einen Funktor. Es ist für Haskell so selbstverständlich, dass der Bediener überlastet werden muss, um dies zu verhindern.
Martin Capodici
17
Ich bin mir nicht sicher, warum Menschen auf Überladung fixiert sind. Die ursprüngliche Frage geht es nicht um Überladung. Für mich geht es um Operatoren als erstklassige Werte. Um $operator1 = +einen Ausdruck zu schreiben und dann zu verwenden, müssen Sie die Operatorüberladung überhaupt nicht verwenden !
Andres F.
16

Es gibt viele Sprachen, die eine Art Metaprogrammierung ermöglichen . Insbesondere wundert es mich, dass keine Antwort auf die Lisp- Sprachfamilie kommt.

Aus Wikipedia:

Metaprogrammierung ist das Schreiben von Computerprogrammen mit der Fähigkeit, Programme als ihre Daten zu behandeln.

Später im Text:

Lisp ist wahrscheinlich der Inbegriff einer Sprache mit Metaprogrammierungsmöglichkeiten, sowohl wegen ihres historischen Vorrangs als auch wegen der Einfachheit und Kraft ihrer Metaprogrammierung.

Lisp-Sprachen

Es folgt eine kurze Einführung in Lisp.

Eine Möglichkeit, Code zu sehen, besteht in einer Reihe von Anweisungen: Tun Sie dies, dann tun Sie das, dann tun Sie das andere ... Dies ist eine Liste! Eine Liste der Aufgaben des Programms. Und natürlich können Listen in Listen enthalten sein, um Schleifen usw. darzustellen.

Wenn wir eine Liste stellen die Elemente a, b, c, d wie folgt enthalten: (ABCD) erhalten wir etwas , das aussieht wie ein Funktionsaufruf Lisp, wo adie Funktion ist, und b, c, dsind die Argumente. Wenn überhaupt das typische "Hallo Welt!" Programm könnte so geschrieben werden:(println "Hello World!")

Natürlich b, coder dkönnten Listen sein, die auch etwas zu bewerten. Folgendes: (println "I can add :" (+ 1 3) )würde dann drucken "" Ich kann hinzufügen: 4 ".

Ein Programm ist also eine Serie verschachtelter Listen, und das erste Element ist eine Funktion. Die gute Nachricht ist, dass wir Listen manipulieren können! So können wir Programmiersprachen manipulieren.

Der Lisp-Vorteil

Lisps sind weniger Programmiersprachen als vielmehr ein Toolkit zur Erstellung von Programmiersprachen. Eine programmierbare Programmiersprache.

In Lisps ist es nicht nur viel einfacher, neue Operatoren zu erstellen, es ist auch nahezu unmöglich, einige Operatoren in anderen Sprachen zu schreiben, da Argumente ausgewertet werden, wenn sie an die Funktion übergeben werden.

Beispiel: In einer C-ähnlichen Sprache möchten Sie ifselbst einen Operator schreiben , etwa:

my-if(condition, if-true, if-false)

my-if(false, print("I should not be printed"), print("I should be printed"))

In diesem Fall werden beide Argumente in einer Reihenfolge ausgewertet und gedruckt, die von der Reihenfolge der Auswertung der Argumente abhängt.

In Lisps ist das Schreiben eines Operators (wir nennen es ein Makro) und einer Funktion ungefähr dasselbe und wird auf die gleiche Weise verwendet. Der Hauptunterschied besteht darin, dass Parameter für ein Makro nicht ausgewertet werden, bevor sie als Argumente an das Makro übergeben werden. Dies ist wichtig, um einige Operatoren wie die ifoben genannten schreiben zu können .

Realistische Sprachen

Hier wird gezeigt, wie genau dies nicht der Fall ist. Ich empfehle Ihnen jedoch, das Programmieren in einem einzigen Lisp zu versuchen, um mehr zu erfahren. Sie könnten sich zum Beispiel Folgendes ansehen:

  • Schema , ein altes, ziemlich "reines" Lisp mit einem kleinen Kern
  • Common Lisp, ein größeres Lisp mit einem gut integrierten Objektsystem und vielen Implementierungen (es ist ANSI-standardisiert)
  • Schlagen Sie einen getippten Lisp
  • Clojure mein Favorit, die obigen Beispiele waren Clojure-Code. Ein modernes Lisp, das auf der JVM läuft. Es gibt auch einige Beispiele für Clojure-Makros auf SO (aber dies ist nicht der richtige Ausgangspunkt. Ich würde zuerst 4clojure , braveclojure oder clojure koans betrachten ).

Übrigens, Lisp bedeutet LISt Processing.

In Bezug auf Ihre Beispiele

Ich werde Beispiele mit Clojure unten geben:

Wenn Sie eine addFunktion in Clojure schreiben können, können (defn add [a b] ...your-implementation-here... )Sie sie +so benennen (defn + [a b] ...your-implementation-here... ). Dies ist in der Tat das, was in der realen Implementierung gemacht wird (der Körper der Funktion ist etwas komplizierter, aber die Definition ist im Wesentlichen die gleiche wie ich oben geschrieben habe).

Was ist mit der Infixnotation? Nun, Clojure verwendet eine prefix(oder polnische) Notation, so dass wir ein infix-to-prefixMakro erstellen können, das vorangestellten Code in Clojure-Code umwandelt. Welches ist eigentlich überraschend einfach (es tatsächlich eine der ist Makro - Übungen in dem clojure koans)! Es kann auch in freier Wildbahn gesehen werden, siehe z$= . B. Incanter- Makro .

Hier ist die einfachste Version aus den Koans erklärt:

(defmacro infix [form]
  (list (second form) (first form) (nth form 2)))

;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result

Um den Punkt noch weiter zu treiben, zitieren einige Lisp :

„Ein Teil dessen, was Lisp auszeichnet, ist, dass es sich weiterentwickeln soll. Mit Lisp können Sie neue Lisp-Operatoren definieren. Wenn neue Abstraktionen populär werden (zum Beispiel objektorientierte Programmierung), ist es immer einfacher, sie in Lisp zu implementieren. Eine solche Sprache gerät wie die DNA nicht aus der Mode. “

- Paul Graham, ANSI Common Lisp

„In Lisp zu programmieren ist wie mit den Urkräften des Universums zu spielen. Es fühlt sich an wie ein Blitz zwischen den Fingerspitzen. Keine andere Sprache fühlt sich so nah an. “

- Glenn Ehrlich, Straße nach Lisp

nha
quelle
1
Beachten Sie, dass die Metaprogrammierung zwar interessant ist, jedoch nicht erforderlich ist, um die Anforderungen des OP zu unterstützen. Jede Sprache mit Unterstützung für erstklassige Funktionen ist ausreichend.
Andres F.
1
Beispiel zu OPs Frage: (let ((opp # '+)) (print (apply opp' (1 2))))
Kasper van den Berg
1
Keine Erwähnung von Common Lisp?
Coredump
3
Ich erinnere mich an eine Podiumsdiskussion über Sprachen, in der Ken über den Vorrang in der APL sprach und schloss mit "Ich benutze selten Klammern überhaupt!" Und jemand aus dem Publikum schrie: "Das liegt daran, dass Dick sie alle aufgebraucht hat!"
JDługosz
2
In einer Sprache im C ++ - Stil könnten Sie sie erneut implementieren if, aber Sie müssten die Argumente thenund elsemit Lambdas umschließen. PHP und JavaScript haben function(), C ++ hat Lambdas, und es gibt eine Apple-Erweiterung für C mit Lambdas.
Damian Yerrick
9

$test = $number1 $operator1 $number2 $operator2 $number3;

Die meisten Sprachimplementierungen haben einen Schritt, bei dem ein Parser Ihren Code analysiert und daraus einen Baum erstellt. So würde zum Beispiel der Ausdruck 5 + 5 * 8analysiert werden als

  +
 / \
5   *
   / \
  8   8

dank des Wissens des Compilers über die Prioritäten. Wenn Sie die Variablen anstelle von Operatoren eingeben, wird die richtige Reihenfolge der Vorgänge vor dem Ausführen des Codes nicht bekannt sein. Bei den meisten Implementierungen wäre dies ein ernstes Problem, daher lassen die meisten Sprachen dies nicht zu.

Sie können sich natürlich eine Sprache vorstellen, in der der Parser das Obige nur als Folge von Ausdrücken und Operatoren analysiert, die zur Laufzeit sortiert und ausgewertet werden. Vermutlich gibt es dafür einfach nicht viel Anwendung.

Viele Skriptsprachen ermöglichen die Auswertung beliebiger Ausdrücke (oder zumindest beliebiger arithmetischer Ausdrücke wie im Fall von expr) zur Laufzeit. Dort könnten Sie einfach Ihre Zahlen und Operatoren in einem einzigen Ausdruck kombinieren und die Sprache das auswerten lassen. In PHP (und vielen anderen) wird diese Funktion aufgerufen eval.

$test = eval("$number1 $operator1 $number2 $operator2 $number3");

Es gibt auch Sprachen, die die Codegenerierung zur Kompilierungszeit ermöglichen. Der mixin Ausdruck in D kommt meiner Meinung nach , wo ich glaube , dass Sie etwas schreiben könnte wie

test = mixin("number1 " + operator1 + " number2 " + operator2 + "number3");

Hier operator1und operator2müssten Zeichenkettenkonstanten sein, die zum Zeitpunkt des Kompilierens bekannt sind, z. B. Template-Parameter. number1, number2Und number3wurden als normale Laufzeitvariablen gelassen.

In anderen Antworten wurde bereits erläutert, wie ein Operator und eine Funktion je nach Sprache mehr oder weniger dasselbe sind. In der Regel gibt es jedoch einen syntaktischen Unterschied zwischen einem eingebauten Infix-Operator-Symbol +und einem benannten Callable-Like operator1. Ich werde die Details den anderen Antworten überlassen.

MvG
quelle
+1 Sie sollten mit "es ist in PHP mit dem eval()Sprachkonstrukt möglich " beginnen ... Technisch liefert es genau das gewünschte Ergebnis, das in der Frage gestellt wird.
Armfoot
@Armfoot: Es ist schwer zu sagen, wo der Fokus der Frage liegt. Der Titel betont den Aspekt „Variable vom Typ Operator“ und evalbeantwortet diesen Aspekt nicht, da evaldie Operatoren nur Zeichenfolgen sind. Daher begann ich mit einer Erklärung, warum vom Operator eingegebene Variablen Probleme verursachen würden, bevor ich Alternativen diskutiere.
MvG
Ich verstehe Ihre Argumente, aber wenn Sie alles in eine Zeichenfolge einfügen, implizieren Sie im Grunde, dass Variablentypen nicht mehr relevant sind (da PHP als Beispiel diente, scheint dies der Schwerpunkt der Frage zu sein), und letztendlich auch Sie Sie können sie so platzieren, als ob einige dieser Variablen vom Typ Operator wären, und erhalten dabei das gleiche Ergebnis. Deshalb liefert Ihr Vorschlag meines Erachtens die genaueste Antwort auf die Frage.
Armfoot
2

Algol 68 hatte genau diese Funktion. Ihr Beispiel in Algol 68 würde so aussehen:

int number1 = 5;                              ¢ (Typ 'Int') ¢
op operator1 = int ( int a , b ) a + b ; ¢ (Typ nicht existent 'Operator') ¢
Prio Operator1 = 4;
int number2 = 5;                              ¢ (Typ 'Int') ¢
op operator2 = int ( int a , b ) a * b ;  ¢(Typ nicht existent 'Operator') ¢
prio operator2 = 5;
int number3 = 8;                              ¢ (Typ 'Int') ¢

int Test = number1 operator1 Nummer2 operator2 number3 ; ¢ 5 + 5 * 8. ¢

var_dump ( test );

Ihr zweites Beispiel würde so aussehen:

int number4 = 9;
op operator3 = bool ( int a , b ) a < b ;
prio operator3 = 3;
wenn Nummer1 $ Operator3 Nummer4, dann ¢ 5 <9 (wahr) ¢
drucken ( wahr )
fi

Sie werden feststellen, dass die Operatorsymbole definiert und Methodenkörper zugewiesen sind, die die gewünschte Operation enthalten. Die Operatoren und ihre Operanden sind alle eingegeben, und den Operatoren können Prioritäten zugewiesen werden, damit die Auswertung in der richtigen Reihenfolge erfolgt. Möglicherweise stellen Sie auch fest, dass sich die Schriftart des Operator- und des Variablensymbols geringfügig unterscheidet.

Obwohl die Sprache mit Schriften geschrieben wurde, konnten die Maschinen der damaligen Zeit die Schriften (Papierstreifen und Lochkarten) nicht verarbeiten, und es wurde Stropping verwendet. Das Programm würde wahrscheinlich eingegeben werden als:

'INT' NUMBER4 = 9;
'OP' 'OPERATOR3' = 'BOOL' ('INT' A,B) A < B;
'PRIO' 'OPERATOR3' = 3;
'IF' NUMBER1 'OPERATOR3' NUMBER4 'THEN' 'C' 5 < 9 'C'
PRINT('TRUE')
'FI'

Sie können auch interessante Spiele mit der Sprache spielen, wenn Sie Ihre eigenen Symbole für Operatoren definieren, die ich vor vielen Jahren einmal ausgenutzt habe ... [2].


Verweise:

[1] Informelle Einführung in Algol 68 von CHLindsey und SG van der Meulen, Nordholland, 1971 .

[2] Algol 68 Phrases, Ein Tool zur Unterstützung des Compiler-Schreibens in Algol 68 , BC Tompsett, Internationale Konferenz über die Anwendung von Algol 68, 1976 an der University of East Anglia, Norwich, UK .

Brian Tompsett - 汤 汤
quelle