Perché esistono così poche lingue con un 'operatore' di tipo variabile?


46

Intendo in questo modo:

<?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);
?>

Ma anche in questo modo:

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

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

Sembra che nessuna lingua abbia questo - c'è una buona ragione per cui non lo fanno?


28
In generale, ciò che vuoi fare sarebbe coperto da tutte le lingue che supportano una qualche forma di meta-programmazione con qualche tipo lambda, chiusure o funzioni anonime che sarebbe il modo comune di implementare tali funzionalità. Con le lingue in cui i metodi sono cittadini di prima classe, sarai in grado di usarli più o meno identici alle variabili. Sebbene non sia esattamente in quella semplice sintassi che utilizzi qui, poiché nella maggior parte di tali lingue è necessario chiarire che in realtà si desidera chiamare il metodo memorizzato nella variabile.
Thorsten Müller,

7
@MartinMaat I linguaggi funzionali lo fanno molto.
Thorbjørn Ravn Andersen,

7
in haskell, gli operatori sono funzioni come qualsiasi altra funzione. il tipo di (+)è Num a => a -> a -> aIIRC. puoi anche definire le funzioni in modo che possano essere scritte infisse ( a + banziché (+) a b)
sara

5
@enderland: la tua modifica ha completamente cambiato l'obiettivo della domanda. È passato dal chiedere se esistono lingue, al chiedere perché ne esistano così poche. Penso che la tua modifica causerà molti lettori confusi.
Bryan Oakley,

5
@enderland: mentre è vero, cambiare completamente argomento serve solo a confondere. Se è fuori tema, la community voterà per chiuderlo. Nessuna delle risposte (al momento in cui scrivo questo) ha senso per la domanda come è scritta.
Bryan Oakley,

Risposte:


103

Gli operatori sono solo funzioni con nomi divertenti, con qualche sintassi speciale in giro.

In molte lingue, diverse come C ++ e Python, puoi ridefinire gli operatori sovrascrivendo metodi speciali della tua classe. Quindi gli operatori standard (ad es. +) Lavorano secondo la logica fornita (ad es. Concatenando stringhe o aggiungendo matrici o altro).

Poiché tali funzioni che definiscono l'operatore sono solo metodi, puoi passarle come faresti con una funzione:

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

Altre lingue consentono di definire direttamente nuovi operatori come funzioni e di utilizzarli in forma infissa.

-- 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

Sfortunatamente, non sono sicuro che PHP supporti qualcosa del genere, e se supportarlo sarebbe una buona cosa. Utilizzare una funzione semplice, è più leggibile di $foo $operator $bar.


2
@tac: Sì, questo è carino e può anche essere trasferito in altre lingue :) Per quanto riguarda Haskell, quello che mi manca di più è che questo dipartimento è l' $operatore che elimina le parentesi (specialmente più nidificati) - ma questo può funzionare solo con non -variadiche funzioni, escludendo così ad esempio Python e Java. La composizione della funzione unaria di OTOH può essere eseguita facilmente .
9000

6
Non sono mai stato un fan del sovraccarico dell'operatore perché sì, un operatore è solo una funzione con sintassi speciale, ma c'è un contratto implicito che di solito va con gli operatori che non vanno con le funzioni. "+", ad esempio, ha determinate aspettative - precedenza dell'operatore, commutatività, ecc. - e andare contro tali aspettative è una strada sicura per confondere le persone e generare bug. Una ragione per cui, anche se adoro javascript, avrei preferito distinguere tra + per aggiunta e concatenazione. Perl ce l'aveva proprio lì.
fool4jesus

4
Nota che Python ha un operatormodulo standard che ti consente di scrivere action = operator.adde che funziona per qualsiasi tipo che definisce +(non solo int).
dan04

3
In Haskell +funziona sulla tabella dei tipi Num in modo da poter implementare +per qualsiasi nuovo tipo di dati creato, ma allo stesso modo in cui si fa qualsiasi altra cosa, ad esempio fmap per un funzione. È così naturale per Haskell consentire agli operatori di sovraccaricare che dovrebbero lavorare sodo per non permetterlo!
Martin Capodici,

17
Non sono sicuro del motivo per cui le persone vengono fissate sul sovraccarico. La domanda originale non riguarda il sovraccarico. Mi sembra che gli operatori siano valori di prima classe. Per scrivere $operator1 = +e poi usare un'espressione non è necessario usare l'overloading dell'operatore !
Andres F.

16

Esistono molte lingue che consentono una sorta di metaprogrammazione . In particolare, sono sorpreso di non avere risposta parlando della famiglia di lingue Lisp .

Da Wikipedia:

La metaprogrammazione è la scrittura di programmi per computer con la capacità di trattare i programmi come i loro dati.

Più avanti nel testo:

Lisp è probabilmente il linguaggio per antonomasia con strutture di metaprogrammazione, sia per la sua precedenza storica sia per la semplicità e la potenza della sua metaprogrammazione.

Lingue Lisp

Segue una breve introduzione a Lisp.

Un modo per vedere il codice è come una serie di istruzioni: fai questo, poi fallo, poi fai quest'altra cosa ... Questa è una lista! Un elenco di cose da fare per il programma. E ovviamente puoi avere liste all'interno di liste per rappresentare loop e così via.

Se rappresentiamo una lista contenente gli elementi a, b, c, d come questo: (abcd) otteniamo qualcosa che assomiglia a una chiamata di funzione Lisp, in cui aè la funzione, e b, c, dsono gli argomenti. In realtà il tipico "Hello World!" il programma potrebbe essere scritto in questo modo:(println "Hello World!")

Certo b, co dpotrebbero essere elenchi che valutano anche qualcosa. Quanto segue: (println "I can add :" (+ 1 3) )stampa quindi "" Posso aggiungere: 4 ".

Quindi, un programma è una serie di elenchi nidificati e il primo elemento è una funzione. La buona notizia è che possiamo manipolare le liste! Quindi possiamo manipolare i linguaggi di programmazione.

Il vantaggio di Lisp

Lisps non è tanto un linguaggio di programmazione quanto un kit di strumenti per creare linguaggi di programmazione. Un linguaggio di programmazione programmabile.

Non è molto più semplice creare nuovi operatori in Lisps, ma è anche quasi impossibile scrivere alcuni operatori in altre lingue perché gli argomenti vengono valutati quando passati alla funzione.

Ad esempio in un linguaggio di tipo C diciamo che vuoi scrivere iftu stesso un operatore, qualcosa del tipo:

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

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

In questo caso entrambi gli argomenti verranno valutati e stampati, in un ordine che dipende dall'ordine di valutazione degli argomenti.

In Lisps, scrivere un operatore (lo chiamiamo macro) e scrivere una funzione è più o meno la stessa cosa e viene usato allo stesso modo. La differenza principale è che i parametri di una macro non vengono valutati prima di essere passati come argomenti alla macro. Questo è essenziale per poter scrivere alcuni operatori, come ifsopra.

Lingue del mondo reale

Mostrando come sia esattamente un po 'fuori portata qui, ma ti incoraggio a provare a programmare in un Lisp per saperne di più. Ad esempio potresti dare un'occhiata a:

  • Schema , un vecchio, abbastanza "puro" Lisp con un piccolo nucleo
  • Common Lisp, un Lisp più grande con un sistema di oggetti ben integrato e molte implementazioni (è standardizzato ANSI)
  • Racchetta un Lisp digitato
  • Clojure il mio preferito, gli esempi sopra erano il codice Clojure. Un moderno Lisp in esecuzione sulla JVM. Ci sono alcuni esempi di macro Clojure anche su SO (ma questo non è il punto giusto da cui iniziare. Guarderei inizialmente koans 4clojure , braveclojure o clojure )).

Oh, a proposito, Lisp significa LISt Processing.

Per quanto riguarda i tuoi esempi

Ho intenzione di dare esempi usando Clojure di seguito:

Se riesci a scrivere una addfunzione in Clojure (defn add [a b] ...your-implementation-here... ), puoi chiamarla +così (defn + [a b] ...your-implementation-here... ). Questo è in effetti ciò che viene fatto nella vera implementazione (il corpo della funzione è un po 'più coinvolto ma la definizione è essenzialmente la stessa di quella che ho scritto sopra).

Che dire della notazione infix? Bene Clojure usa una prefixnotazione (o polacca), quindi potremmo creare una infix-to-prefixmacro che trasformerebbe il codice con prefisso in codice Clojure. Il che è sorprendentemente semplice (in realtà è uno degli esercizi macro nei koan clojure)! Può anche essere visto in natura, ad esempio vedi macro Incanter$= .

Ecco la versione più semplice dei koan spiegati:

(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

Per guidare ulteriormente il punto, alcune citazioni di Lisp :

“Parte di ciò che distingue Lisp è che è progettato per evolversi. Puoi usare Lisp per definire nuovi operatori Lisp. Man mano che nuove astrazioni diventano popolari (programmazione orientata agli oggetti, ad esempio), risulta sempre facile implementarle in Lisp. Come il DNA, un linguaggio del genere non passa di moda. "

- Paul Graham, ANSI Common Lisp

“Programmare in Lisp è come giocare con le forze primordiali dell'universo. Sembra un lampo tra le punte delle dita. Nessun'altra lingua si sente nemmeno vicina. "

- Glenn Ehrlich, Road to Lisp


1
Si noti che la metaprogrammazione, sebbene interessante, non è necessaria per supportare ciò che l'OP sta chiedendo. Qualsiasi lingua con supporto per funzioni di prima classe è sufficiente.
Andres F.

1
Esempio alla domanda di OP: (let ((opp # '+)) (stampa (applica opp' (1 2))))
Kasper van den Berg

1
Nessuna menzione di Common Lisp?
coredump,

3
Ricordo in una tavola rotonda sulle lingue, Ken parlava di precedenza in APL e concluse con "Non uso quasi mai le parentesi!" E qualcuno del pubblico urlò, "questo perché Dick li ha usati tutti!"
JDługosz,

2
In un linguaggio in stile C ++, potresti reimplementare if, ma dovresti avvolgere gli argomenti thene elsecon lambdas. PHP e JavaScript hanno function(), C ++ ha lambdas e esiste un'estensione di Apple a C con lambdas.
Damian Yerrick,

9

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

La maggior parte delle implementazioni linguistiche prevede un passaggio in cui un parser analizza il codice e crea un albero da esso. Ad esempio, l'espressione 5 + 5 * 8verrebbe analizzata come

  +
 / \
5   *
   / \
  8   8

grazie alla conoscenza del compilatore sulle precedenti. Se le alimentassi con variabili al posto degli operatori, non conoscerebbe l'ordine corretto delle operazioni prima di eseguire il codice. Per la maggior parte delle implementazioni sarebbe un problema serio, quindi la maggior parte delle lingue non lo consente.

Ovviamente potresti concepire un linguaggio in cui il parser analizza quanto sopra come una sequenza di espressioni e operatori, da ordinare e valutare in fase di esecuzione. Presumibilmente non c'è molta applicazione per questo.

Molti linguaggi di scripting consentono la valutazione di espressioni arbitrarie (o almeno espressioni aritmetiche arbitrarie come nel caso di expr) in fase di esecuzione. Lì potresti semplicemente combinare i tuoi numeri e operatori in un'unica espressione e lasciare che il linguaggio lo valuti. In PHP (e molti altri) viene chiamata quella funzione eval.

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

Esistono anche lingue che consentono la generazione di codice in fase di compilazione. L' espressione mixin in D viene in mente, dove credo che si potrebbe scrivere qualcosa di simile

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

Qui operator1e operator2dovrebbero essere costanti di stringa che sono note al momento della compilazione, ad esempio i parametri del modello. number1, number2E number3sono stati lasciati come normali variabili di runtime.

Altre risposte hanno già discusso dei vari modi in cui un operatore e una funzione sono più o meno la stessa cosa, a seconda della lingua. Ma di solito c'è una differenza sintattica tra un simbolo di operatore infisso incorporato +e un nome richiamabile come operator1. Lascio i dettagli a quelle altre risposte.


+1 Dovresti iniziare con "è possibile in PHP con il eval()costrutto del linguaggio " ... Tecnicamente fornisce esattamente il risultato desiderato richiesto nella domanda.
Armfoot,

@Armfoot: è difficile dire dove si concentri la questione. Il titolo enfatizza l'aspetto della "variabile di tipo operatore" e evalnon risponde a tale aspetto, poiché per evalgli operatori sono solo stringhe. Pertanto, ho iniziato con una spiegazione del perché le variabili digitate dall'operatore avrebbero causato problemi, prima di iniziare a discutere di alternative.
MvG

Capisco i tuoi punti, ma ritengo che inserendo tutto in una stringa, sostanzialmente stai sottintendendo che i tipi di variabili non sono più rilevanti (dal momento che PHP è stato usato per esemplificare, questo sembra essere il focus della domanda), e alla fine, tu possono posizionarli nello stesso modo in cui alcune di quelle variabili fossero di "operatore di tipo" pur ottenendo lo stesso risultato ... Ecco perché credo che il tuo suggerimento fornisca la risposta più accurata alla domanda.
Armfoot,

2

Algol 68 aveva esattamente questa caratteristica. Il tuo esempio in Algol 68 sarebbe simile al seguente:

int number1 = 5;                              ¢ (Tipo 'Int') ¢
op operator1 = int ( int a , b ) a + b ; ¢ (Tipo 'Operatore' inesistente) ¢
prio operator1 = 4;
int number2 = 5;                              ¢ (Tipo 'Int') ¢
op operator2 = int ( int a , b ) a * b ;  ¢(Tipo 'Operatore' inesistente) ¢
prio operator2 = 5;
int number3 = 8;                              ¢ (Digitare 'Int') ¢

int prova = number1 operator1 numero2 operator2 number3 ; ¢ 5 + 5 * 8. ¢

var_dump ( test );

Il tuo secondo esempio sarebbe simile al seguente:

int number4 = 9;
op operator3 = bool ( int a , b ) a < b ;
prio operator3 = 3;
se numero1 $ operatore3 numero4, quindi ¢ 5 <9 (vero) ¢
stampa ( vero )
fi

Noterai che i simboli dell'operatore sono definiti e assegnati ai corpi del metodo che contengono l'operazione desiderata. Gli operatori e il loro operando sono tutti digitati e agli operatori possono essere assegnate priorità in modo che la valutazione avvenga nell'ordine corretto. Potresti anche notare che c'è una leggera differenza nel font tra il simbolo dell'operatore e un simbolo variabile.

In realtà, anche se la lingua è scritta usando i caratteri, le macchine del giorno non sono in grado di gestire i caratteri (nastro di carta e carte perforate) e viene utilizzato il trucchetto . Il programma verrebbe probabilmente inserito come:

'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'

Puoi anche giocare a giochi interessanti con la lingua quando puoi definire i tuoi simboli per gli operatori, che ho sfruttato una volta, molti anni fa ... [2].


Riferimenti:

[1] Introduzione informale all'Algol 68 di CHLindsey e SG van der Meulen, Olanda Settentrionale, 1971 .

[2] Algol 68 Phrases, uno strumento per aiutare la scrittura di compilatori in Algol 68 , BC Tompsett, Conferenza internazionale sulle applicazioni di Algol 68, Università di East Anglia, Norwich, Regno Unito, 1976 ..

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.