it-swarm.com.de

Warum gibt es so wenige Sprachen mit einem variablen 'Operator'?

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 dies nicht tun?

45
kgongonowdoe

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

In vielen Sprachen, so unterschiedlich wie C++ und Python, können Sie Operatoren neu definieren, indem Sie spezielle Methoden Ihrer Klasse überschreiben. Dann Standardoperatoren (z. B. +) arbeiten gemäß 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 in Infixform 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 unterstützt so etwas und wenn es eine gute Sache wäre, es zu unterstützen. Verwenden Sie eine einfache Funktion, sie ist besser lesbar als $foo $operator $bar.

103
9000

Es gibt viele Sprachen, die eine Art Metaprogrammierung erlauben. Insbesondere bin ich überrascht, dass keine Antwort über die LISP Sprachfamilie spricht.

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 die Quintessenz der Metaprogrammierfunktionen, sowohl aufgrund seines historischen Vorrangs als auch aufgrund der Einfachheit und Leistungsfähigkeit seiner 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 diese andere Sache ... Dies ist eine Liste! Eine Liste der Aufgaben des Programms. Und natürlich können Sie Listen in Listen haben, um Schleifen usw. darzustellen.

Wenn wir eine Liste mit den Elementen a, b, c, d wie folgt darstellen: (abcd) erhalten wir etwas, das wie ein LISP-Funktionsaufruf aussieht, wobei a die Funktion ist und b , c, d sind die Argumente. Ist in der Tat die typische "Hallo Welt!" Programm könnte so geschrieben werden: (println "Hello World!")

Natürlich können b, c oder d Listen sein, die auch etwas auswerten. Folgendes: (println "I can add :" (+ 1 3) ) Würde dann "" Ich kann hinzufügen: 4 "drucken.

Ein Programm ist also eine Reihe 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 zum Erstellen von Programmiersprachen. Eine programmierbare Programmiersprache.

Dies ist in Lisps 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.

Nehmen wir zum Beispiel an, Sie möchten in einer C-ähnlichen Sprache selbst einen if -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 das Schreiben einer Funktion ungefähr dasselbe und wird auf dieselbe 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 das obige if schreiben zu können.

Sprachen der realen Welt

Zeigen, wie genau hier etwas außerhalb des Anwendungsbereichs liegt, aber ich empfehle Ihnen, die Programmierung in einem LISP zu versuchen, um mehr zu erfahren. Zum Beispiel könnten Sie einen Blick darauf werfen:

  • 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)
  • Racket ein typisierter LISP
  • Clojure mein Favorit, die obigen Beispiele waren Clojure-Code. Ein modernes LISP, das auf der JVM ausgeführt wird. Es gibt auch einige Beispiele für Clojure-Makros auf ALSO (aber dies ist nicht der richtige Ausgangspunkt. Ich würde mir 4clojure ansehen =, Braveclojure oder Clojure Koans zuerst)).

Übrigens bedeutet LISP LISt-Verarbeitung.

In Bezug auf Ihre Beispiele

Ich werde unten Beispiele mit Clojure geben:

Wenn Sie eine add -Funktion in Clojure (defn add [a b] ...your-implementation-here... ) Schreiben können, können Sie sie + So (defn + [a b] ...your-implementation-here... ) Nennen. Dies ist in der Tat das, was in der reale Implementierung gemacht wird (der Hauptteil der Funktion ist etwas komplizierter, aber die Definition ist im Wesentlichen dieselbe wie oben geschrieben).

Was ist mit der Infix-Notation? Nun, Clojure verwendet eine prefix (oder polnische) Notation, sodass wir ein infix-to-prefix - Makro erstellen können, das Präfixcode in Clojure-Code umwandelt. Was eigentlich überraschend einfach ist (es ist tatsächlich eine der Makroübungen in den Clojure-Koans)! Es kann auch in freier Wildbahn gesehen werden, siehe beispielsweise 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 voranzutreiben, einige LISP-Zitate :

„Ein Teil dessen, was LISP auszeichnet, ist, dass es darauf ausgelegt ist, sich weiterzuentwickeln. Mit LISP können Sie neue LISP-Operatoren definieren. Wenn neue Abstraktionen populär werden (z. B. objektorientierte Programmierung), stellt sich heraus, dass es immer einfach ist, sie in LISP zu implementieren. Eine solche Sprache kommt wie die DNA nicht aus der Mode. “

- Paul Graham, ANSI Common LISP

„Das Programmieren in LISP ist wie das Spielen mit den Urkräften des Universums. Es fühlt sich an wie ein Blitz zwischen Ihren Fingerspitzen. Keine andere Sprache fühlt sich auch nur nahe. “

- Glenn Ehrlich, Weg zum LISP

16
nha

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

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

  +
 / \
5   *
   / \
  8   8

dank des Wissens des Compilers über Präzedenzfälle. Wenn Sie Variablen anstelle von Operatoren eingeben, wird die richtige Reihenfolge der Operationen vor dem Ausführen des Codes nicht bekannt. Für die meisten Implementierungen wäre dies ein ernstes Problem, daher erlauben die meisten Sprachen dies nicht.

Sie können sich natürlich eine Sprache vorstellen, in der der Parser das Obige nur als eine 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önnen Sie einfach Ihre Zahlen und Operatoren zu einem einzigen Ausdruck kombinieren und die Sprache dies auswerten lassen. In PHP (und vielen anderen)) heißt diese Funktion eval .

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

Es gibt auch Sprachen, die die Codegenerierung zur Kompilierungszeit ermöglichen. Das Mixin-Ausdruck in D kommt mir in den Sinn, wo ich glaube, dass Sie so etwas schreiben könnten

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

Hier operator1 und operator2 müssten Zeichenfolgenkonstanten sein, die zur Kompilierungszeit bekannt sind, z. Vorlagenparameter. number1, number2 und number3 wurden als normale Laufzeitvariablen belassen.

In anderen Antworten wurden bereits die verschiedenen Arten erörtert, wie ein Operator und eine Funktion je nach Sprache mehr oder weniger dasselbe sind. Normalerweise gibt es jedoch einen syntaktischen Unterschied zwischen einem integrierten Infix-Operatorsymbol wie + und ein benannter Aufruf wie operator1. Ich überlasse die Details diesen anderen Antworten.

9
MvG

ALGOL 68 hatte genau diese Funktion. Ihr Beispiel in ALGOL 68 würde folgendermaßen aussehen:

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

int test = number1 operator1 number2 operator2 number3; ¢ 5 + 5 * 8. ¢

var_dump ( test);

Ihr zweites Beispiel würde folgendermaßen aussehen:

int number4 = 9;
op operator3 = bool ( int a, b) a < b;
prio operator3 = 3;
if number1 $ operator3 number4 dann ¢ 5 <9 (wahr) ¢
print ( true )
fi

Sie werden feststellen, dass die Operatorsymbole definiert und Methodenkörper zugewiesen sind, die die gewünschte Operation enthalten. Die Operatoren und ihr Operand sind alle typisiert, 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 zwischen dem Operatorsymbol und einem Variablensymbol geringfügig unterscheidet.

Obwohl die Sprache mit Schrift geschrieben wurde, konnten die Maschinen des Tages die Schriftarten (Papierband und Lochkarten) nicht verarbeiten, und Strippen wurde verwendet. Das Programm würde wahrscheinlich wie folgt eingegeben:

'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 können, die ich vor vielen Jahren einmal ausgenutzt habe ... [2].


Verweise:

[1] Informelle Einführung in ALGOL 68 von C. H. Lindsey und S. G. van der Meulen, Nordholland, 1971 .

[2] ALGOL 68 Phrases, Ein Tool zur Unterstützung des Compiler-Schreibens in ALGOL 68, B.C. Tompsett, Internationale Konferenz über die Anwendungen von ALGOL 68, Universität East Anglia, Norwich, Großbritannien, 1976. .