Perché il binding non è una funzionalità nativa nella maggior parte delle lingue?


11

L'IMHO che lega una variabile a un'altra variabile o un'espressione è uno scenario molto comune in matematica. In effetti, all'inizio, molti studenti pensano che l'operatore di assegnazione (=) sia una sorta di associazione. Ma nella maggior parte delle lingue, l'associazione non è supportata come funzionalità nativa. In alcune lingue come C #, l'associazione è supportata in alcuni casi con alcune condizioni soddisfatte.

Ma IMHO implementarlo come funzionalità nativa era semplice come cambiare il seguente codice:

int a,b,sum;
sum := a + b;
a = 10;
b = 20;
a++;

a questa-

int a,b,sum;
a = 10;
sum = a + b;
b = 20;
sum = a + b;
a++;
sum = a + b;

Significa posizionare l'istruzione di rilegatura come assegnazioni dopo ogni istruzione che modifica i valori di una qualsiasi delle variabili contenute nell'espressione sul lato destro. Dopodiché, verranno eseguite le istruzioni ridondanti (o l'ottimizzazione dell'assemblaggio dopo la compilazione).

Quindi, perché non è supportato in modo nativo nella maggior parte delle lingue. Specialmente nella famiglia di lingue C?

Aggiornare:

Da opinioni diverse, penso che dovrei definire questa proposta "vincolante" più precisamente-

  • Questo è un modo vincolante. Solo la somma è legata a + b, non viceversa.
  • Lo scopo dell'associazione è locale.
  • Una volta stabilito l'associazione, non può essere modificato. Significato, una volta che la somma è legata a + b, la somma sarà sempre a + b.

Spero che l'idea sia più chiara ora.

Aggiornamento 2:

Volevo solo questa funzione P # . Spero che ci sarà in futuro.


14
Forse perché qualsiasi sviluppatore di compilatori che ha provato ad aggiungere questa funzione a C è stato cacciato e sparato.
Pete Wilson,

Sto parlando di rilegatura unidirezionale (da destra a sinistra), non bidirezionale. L'associazione interesserà sempre solo una variabile.
Gulshan,

2
Per quello che vale, questo tipo di punto di vista programmatico è in aumento: la programmazione reattiva. Quello che stai descrivendo è anche incarnato da programmi di fogli di calcolo come Excel, che sono essenzialmente dati vincolanti (o programmazione reattiva) sugli steroidi.
Mike Rosenblum,

11
Potrebbe non essere una caratteristica della maggior parte dei linguaggi di programmazione, ma è una caratteristica del linguaggio di programmazione più popolare : Excel.
Jörg W Mittag,

2
Grazie agli alberi delle espressioni, questo è già possibile in C #. Ne ho scritto un
HappyNomad

Risposte:


9

Stai confondendo la programmazione con la matematica. Neanche la programmazione funzionale è interamente matematica, anche se prende in prestito molte idee e le trasforma in qualcosa che può essere eseguito e utilizzato per la programmazione. La programmazione imperativa (che include la maggior parte dei linguaggi ispirati al C, eccezioni notevoli come JavaScript e gli additivi più recenti a C #) non ha quasi nulla a che fare con la matematica, quindi perché queste variabili dovrebbero comportarsi come variabili in matematica?

Devi considerare che questo non è sempre quello che vuoi. Così tante persone sono morse da chiusure create in loop proprio perché le chiusure mantengono la variabile, non una copia del suo valore ad un certo punto, ovvero for (i = 0; i < 10; i++) { var f = function() { return i; }; /* store f */ }creano dieci chiusure che restituiscono 9. Quindi avresti bisogno di supportare entrambi i modi, il che significa due volte il costo sul "budget di complessità" e ancora un altro operatore. Forse anche incompatibilità tra il codice che usa questo e il codice che non lo usa, a meno che il sistema di tipi non sia abbastanza sofisticato (più complessità!).

Inoltre, implementarlo in modo efficace è molto difficile. L'implementazione ingenua aggiunge un sovraccarico costante ad ogni incarico, che può sommarsi rapidamente nei programmi imperativi. Altre implementazioni potrebbero ritardare gli aggiornamenti fino a quando la variabile non viene letta, ma ciò è significativamente più complesso e ha ancora un sovraccarico anche quando la variabile non viene mai più letta. Un compilatore sufficientemente intelligente può ottimizzare entrambi, ma i compilatori sufficientemente intelligenti sono rari e richiedono molti sforzi per creare (nota che non è sempre così semplice come nel tuo esempio, specialmente quando le variabili hanno un ampio raggio d'azione e il multithreading entra in gioco!).

Si noti che la programmazione reattiva è fondamentalmente su questo (per quanto posso dire), quindi esiste. Non è poi così comune nei linguaggi di programmazione tradizionali. E scommetto che alcuni dei problemi di implementazione che ho elencato nel paragrafo precedente sono risolti.


Penso che tu abbia 3 punti: 1) Non è una programmazione in stile imperativo. In questi giorni la maggior parte delle lingue non si limita ad alcuni paradigmi. Non credo che questo stile di paradigma sia una buona logica. 2) Complessità. Hai dimostrato che molte cose più complesse sono già supportate. Perché non questo? So che gli operatori che non hanno quasi nulla sono supportati. E questa è una funzionalità totalmente nuova. Quindi, come può esserci il problema di compatibilità. Non sto cambiando o spazzando via qualcosa. 3) Implementazione difficile. Penso che gli attuali compilatori abbiano già la possibilità di ottimizzarlo.
Gulshan,

1
@Gulshan: (1) Nessun paradigma di programmazione è la matematica. FP è vicino, ma FP è relativamente raro e le lingue FP impure con variabili mutabili non trattano queste variabili come se fossero anche matematiche. L'unico paradigma in cui ciò esiste è la programmazione reattiva, ma la programmazione reattiva non è nota o utilizzata ampiamente (nei linguaggi di programmazione tradizionali). (2) Tutto ha un certo costo di complessità e un valore. Questo ha un costo piuttosto elevato e un valore relativamente basso di IMHO, tranne in alcuni domini, quindi non è la prima cosa che aggiungerei alla mia lingua a meno che non abbia come target specifico questi domini.

Perché non avere un altro strumento nella nostra scatola? Una volta che c'è uno strumento, le persone lo useranno. Una domanda. Se un linguaggio come C o C ++ vuole implementare questa funzione e chiedere la tua opinione, diresti: "Non darlo. Perché sarà troppo difficile per te e la gente si sbaglierà con questo".
Gulshan,

@Gulshan: Riguardo (3): non è sempre (per non dire, raramente) facile come nel tuo esempio. Mettilo in ambito globale e improvvisamente hai bisogno di ottimizzazioni del tempo di collegamento. Quindi aggiungi il collegamento dinamico e non puoi nemmeno fare nulla in Compiletime. È necessario un compilatore molto intelligente e un runtime molto intelligente, incluso JIT, per farlo bene. Considera anche la quantità di incarichi in ogni programma non banale. Né tu né io possiamo scrivere questi componenti. Ci sono al massimo poche persone che possono e sono interessate a questo argomento. E potrebbero avere cose migliori da fare.

@Gulshan: Chiederei loro di non aggiungere funzionalità alla incredibilmente complessa bestia C ++ già, o chiederei loro di non provare a usare C per cose oltre la programmazione del sistema (che non è uno dei domini in cui questo è molto utile ). A parte questo, sono tutto per entusiasmanti nuove funzionalità, ma solo quando c'è abbastanza del budget di complessità rimasto (molte lingue hanno sempre esaurito le loro) e la funzione è utile per ciò che la lingua è destinata a fare - come ho detto prima, lì sono solo alcuni domini in cui questo è utile.

3

Si adatta molto male alla maggior parte dei modelli di programmazione. Rappresenterebbe una sorta di azione a distanza completamente incontrollata, in cui si potrebbe distruggere il valore di centinaia o migliaia di variabili e campi oggetto effettuando una singola assegnazione.


Quindi suggerirei di stabilire una regola secondo cui la variabile associata, ovvero il lato sinistro, non verrà modificata in nessun altro modo. E quello di cui sto parlando è solo un modo vincolante, non bidirezionale. Se segui la mia domanda, puoi vedere. Pertanto, nessun'altra variabile sarà interessata.
Gulshan,

Non importa. Ogni volta che scrivi ao b, devi considerare il suo impatto su ogni singolo luogo sumutilizzato e ogni luogo che leggi sumdevi considerare cosa ae cosa bstanno facendo. Per casi non banali, ciò potrebbe complicarsi, specialmente se l'espressione effettiva associata sumpuò cambiare in fase di esecuzione.
giovedì

Vorrei raccomandare la regola secondo cui l'espressione di associazione non può essere modificata una volta eseguita l'associazione. Anche l'incarico sarà impossibile. Sarà come una semi-costante una volta legata a un'espressione. Ciò significa che, una volta che la somma è legata a + b, sarà sempre a + b, durante il resto del programma.
Gulshan,

3

Sai, ho questa fastidiosa sensazione che la programmazione reattiva potrebbe essere interessante in un ambiente Web 2.0. Perché la sensazione? Bene, ho questa pagina che è principalmente una tabella che cambia continuamente in risposta agli eventi onClick di una cella di tabella. E i clic delle celle spesso significano cambiare la classe di tutte le celle in una colonna o riga; e ciò significa cicli infiniti di getRefToDiv () e simili, per trovare altre celle correlate.

IOW, molte delle ~ 3000 righe di JavaScript che ho scritto non fanno altro che individuare oggetti. Forse la programmazione reattiva potrebbe fare tutto ciò a costi contenuti; e con un'enorme riduzione delle righe di codice.

Cosa ne pensate ragazzi? Sì, noto che la mia tabella ha molte funzioni simili a fogli di calcolo.


3

Penso che quello che stai descrivendo sia chiamato un foglio di calcolo:

A1=5
B1=A1+1
A1=6

... quindi valutando i B1ritorni 7.

MODIFICARE

Il linguaggio C è talvolta chiamato "assembly portatile". È una lingua imperativa, mentre i fogli di calcolo, ecc., Sono lingue dichiarative. Dire B1=A1+1e aspettarsi B1di rivalutare quando si cambia A1è decisamente dichiarativo. I linguaggi dichiarativi (di cui i linguaggi funzionali sono un sottoinsieme) sono generalmente considerati linguaggi di livello superiore, poiché sono più lontani da come funziona l'hardware.

In una nota correlata, i linguaggi di automazione come la logica ladder sono in genere dichiarativi. Se scrivi un ramo di logica che dice output A = input B OR input Cche rivaluterà costantemente tale affermazione e Apuò cambiare ogni volta Bo Ccambiamenti. Altri linguaggi di automazione come il diagramma a blocchi funzione (che potresti avere familiarità con l'utilizzo di Simulink) sono anch'essi dichiarativi ed eseguono continuamente.

Alcune apparecchiature di automazione (incorporate) sono programmate in C, e se si tratta di un sistema in tempo reale, probabilmente ha un loop infinito che riesegue ripetutamente la logica, in modo simile a come viene eseguita la logica ladder. In tal caso, all'interno del tuo ciclo principale potresti scrivere:

A = B || C;

... e dal momento che viene eseguito continuamente, diventa dichiarativo. Asarà costantemente rivalutato.


3

C, C ++, Objective-C:

I blocchi offrono la funzione di rilegatura che stai cercando.

Nel tuo esempio:

somma: = a + b;

stai impostando suml'espressione a + bin un contesto in cui ae bsono variabili esistenti. Puoi fare esattamente questo con un "blocco" (aka chiusura, aka espressione lambda) in C, C ++ o Objective-C con le estensioni di Apple (pdf):

__block int a = 0, b = 0;           // declare a and b
int (^sum)(void);                   // declare sum
sum = ^(void){return a + b;};       // sum := a + b

Questo imposta sumsu un blocco che restituisce la somma di aeb. L' __blockidentificatore della classe di archiviazione indica che ae bpuò cambiare. Dato quanto sopra, possiamo eseguire il seguente codice:

printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
a = 10;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
b = 32;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());
a++;
printf("a=%d\t b=%d\t sum=%d\n", a, b, sum());

e ottieni l'output:

a=0      b=0     sum=0
a=10     b=0     sum=10
a=10     b=32    sum=42
a=11     b=32    sum=43

L'unica differenza tra l'uso di un blocco e il "bind" che proponi è la coppia vuota tra parentesi sum(). La differenza tra sume sum()è la differenza tra un'espressione e il risultato di quell'espressione. Si noti che, come per le funzioni, le parentesi non devono essere vuote: i blocchi possono assumere parametri proprio come le funzioni.


2

C ++

Aggiornato per essere generico. Parametrizzato su ritorno e tipi di input. Può fornire qualsiasi operazione binaria che soddisfi i tipi parametrizzati. Il codice calcola il risultato su richiesta. Cerca di non ricalcolare i risultati se riesce a cavarsela. Eliminalo se questo è indesiderabile (a causa di effetti collaterali, perché gli oggetti contenuti sono grandi, a causa di qualsiasi cosa.)

#include <iostream>

template <class R, class A, class B>
class Binding {
public:
    typedef R (*BinOp)(A, B);
    Binding (A &x, B &y, BinOp op)
        : op(op)
        , rx(x)
        , ry(y)
        , useCache(false)
    {}
    R value () const {
        if (useCache && x == rx && y == ry) {
            return cache;
        }
        x = rx;
        y = ry;
        cache = op(x, y);
        useCache = true;
        return cache;
    }
    operator R () const {
        return value();
    }
private:
    BinOp op;
    A &rx;
    B &ry;
    mutable A x;
    mutable B y;
    mutable R cache;
    mutable bool useCache;
};

int add (int x, int y) {
    return x + y;
}

int main () {
    int x = 1;
    int y = 2;
    Binding<int, int, int> z(x, y, add);
    x += 55;
    y *= x;
    std::cout << (int)z;
    return 0;
}

Anche se non risponde alla domanda, mi piace l'idea che hai presentato. Può esserci una versione più generica?
Gulshan,

@Gulshan: Aggiornato
Thomas Eding il
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.