Perché l'associazione non è una funzionalità nativa nella maggior parte delle lingue?

11

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

Ma IMHO implementare questa funzionalità nativa è semplice come cambiare il codice seguente:

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

a questo -

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

Significa porre l'istruzione di legame come compito dopo ogni istruzione che modifica i valori di una qualsiasi delle variabili contenute nell'espressione sul lato destro. Dopodiché, il taglio delle istruzioni ridondanti (o l'ottimizzazione in assemblaggio dopo la compilazione) lo farà.

Quindi, perché non è supportato nativamente nella maggior parte delle lingue. Specialmente nella famiglia C delle lingue?

Aggiornamento:

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

  • Questo è un modo vincolante. Solo la somma è vincolata a + b, non viceversa.
  • L'ambito dell'associazione è locale.
  • Una volta stabilita l'associazione, non può essere modificata. Significato, una volta che la somma è vincolata 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 sarà lì in futuro.

    
posta Gulshan 09.04.2011 - 17:27
fonte

6 risposte

9

Stai confondendo la programmazione con la matematica. Nemmeno 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 a C, le eccezioni notevoli in JavaScript e le aggiunte 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 in particolare perché le chiusure mantengono la variabile, non una copia del suo valore ad un certo punto, cioè for (i = 0; i < 10; i++) { var f = function() { return i; }; /* store f */ } crea dieci chiusure che restituiscono 9 . Quindi dovresti supportare entrambi i modi, il che significa il doppio del costo sul "budget di complessità" e ancora un altro operatore. Forse anche le incompatibilità tra il codice che usa questo e il codice che non lo usa, a meno che il sistema di tipi sia abbastanza sofisticato (più complessità!).

Inoltre, implementare questo modo efficace è molto difficile. L'implementazione ingenua aggiunge un sovraccarico costante all'assegnazione ogni , che può essere rapidamente aggiunta a 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 sufficentemente intelligente può ottimizzare entrambi, ma i compilatori sufficientemente intelligenti sono rari e richiedono molto sforzo per creare (si noti che non è sempre così semplice come nell'esempio, specialmente quando le variabili hanno un ampio ambito e il multithreading entra in gioco!) p>

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

    
risposta data 09.04.2011 - 18:16
fonte
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 creando un singolo compito.

    
risposta data 09.04.2011 - 18:30
fonte
3

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

IOW, molte delle ~ 3000 righe di JavaScript che ho scritto non fanno altro che localizzare gli oggetti. Forse la programmazione reattiva potrebbe fare tutto ciò con un piccolo costo; e con un'enorme riduzione delle linee di codice.

Che ne pensate voi ragazzi di questo? Sì, ho notato che la mia tabella ha un sacco di funzioni simili a fogli di calcolo.

    
risposta data 09.04.2011 - 21:24
fonte
3

Penso che quello che stai descrivendo si chiami un foglio di calcolo:

A1=5
B1=A1+1
A1=6

... quindi la valutazione di B1 restituisce 7.

Modifica

Il linguaggio C viene talvolta chiamato "assembly portatile". È una lingua imperativa, mentre i fogli di calcolo, ecc. Sono linguaggi dichiarativi. Dire B1=A1+1 e prevedere B1 da rivalutare quando cambi A1 è sicuramente dichiarativo. Le lingue dichiarative (di cui le lingue funzionali sono un sottoinsieme) sono generalmente considerate lingue di livello superiore, perché sono più distanti da come funziona l'hardware.

Su una nota correlata, i linguaggi di automazione come la logica ladder sono in genere dichiarativi. Se scrivi un ramo logico che dice output A = input B OR input C , valuterà costantemente quella frase e A può cambiare ogni volta che B o C cambia. Altri linguaggi di automazione come il Function Block Diagram (che potresti avere familiarità con l'utilizzo di Simulink) sono anch'essi dichiarativi e vengono eseguiti continuamente.

Alcune apparecchiature di automazione (incorporate) sono programmate in C e, se si tratta di un sistema in tempo reale, probabilmente ha un ciclo infinito che rielabora ripetutamente la logica, in modo analogo 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 è in esecuzione in ogni momento, diventa dichiarativo. A sarà costantemente rivalutato.

    
risposta data 19.11.2011 - 03:58
fonte
3

C, C ++, Objective-C:

I blocchi forniscono la funzione di rilegatura che stai cercando.

Nel tuo esempio:

sum := a + b;

stai impostando sum sull'espressione a + b in un contesto in cui a e b sono variabili esistenti. Puoi fare esattamente questo con un "blocco" (aka closure, aka lambda expression) in C, C ++ o Objective-C con Elementi 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

Imposta sum su un blocco che restituisce la somma di a e b. Lo specificatore della classe di memoria __block indica che a e b potrebbero cambiare. Considerato 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'utilizzo di un blocco e il "binding" che proponi è la coppia vuota di parentesi in sum() . La differenza tra sum e sum() è la differenza tra un'espressione e il risultato di tale espressione. Nota che, come per le funzioni, le parentesi non devono essere vuote: i blocchi possono prendere i parametri proprio come le funzioni.

    
risposta data 19.11.2011 - 04:32
fonte
2

C ++

Aggiornato per essere generico. Parametrizzato sui tipi di ritorno e 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 può farla franca. Toglilo se questo non è desiderabile (a causa degli 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;
}
    
risposta data 10.04.2011 - 03:33
fonte

Leggi altre domande sui tag