2009-06-17 6 views
10

Durante la riproduzione in giro con D 2.0 Ho trovato il seguente problema:Come usare puri di D 2.0

Esempio 1:

pure string[] run1() 
{ 
    string[] msg; 
    msg ~= "Test"; 
    msg ~= "this."; 
    return msg; 
} 

Questo compila e funziona come previsto.

Quando provo ad avvolgere la matrice di stringhe in una classe che trovo non riesco a far funzionare tutto questo:

class TestPure 
{ 
    string[] msg; 
    void addMsg(string s) 
    { 
     msg ~= s; 
    } 
}; 

pure TestPure run2() 
{ 
    TestPure t = new TestPure(); 
    t.addMsg("Test"); 
    t.addMsg("this."); 
    return t; 
} 

Questo codice non verrà compilato perché la funzione addMsg è impuro. Non riesco a rendere tale funzione pura poiché altera l'oggetto TestPure. Mi manca qualcosa? O è una limitazione?

Di seguito si compila:

pure TestPure run3() 
{ 
    TestPure t = new TestPure(); 
    t.msg ~= "Test"; 
    t.msg ~= "this."; 
    return t; 
} 

Può la ~ = operatore non stata implementata in funzione impura della matrice msg? Come mai il compilatore non si lamenta di ciò nella funzione run1?

+0

Ho cercato di ripulire il tag [tag: pure], perché a volte si riferisce a pure funzioni virtuali, a volte a [puro] (http://beebole.com/pure/) ea volte a [puro] (http://en.wikipedia.org/wiki/Pure_ (programming_language)) - tra gli altri. Ma non so nulla di [tag: d2]. Potresti confermare se la modifica del mio tag è appropriata? Potrebbe [tag: puramente funzionale] funzionare per questa domanda - Ho creato [tag: pure-function], quindi se [tag: purely-functional] funziona penso che sarebbe meglio usare il tag esistente. –

risposta

6

Dalla v2.050, D ha attenuato la definizione di pure per accettare anche le cosiddette funzioni "debolmente pure". Questo si riferisce alle funzioni "do not read or write any global mutable state". Le funzioni debolmente pure sono non le stesse come pure funzioni nel senso della lingua funzionale. L'unica relazione è che fanno vere e proprie funzioni pure, a.k.a. "Puramente" funzioni in grado di chiamare i deboli, come nell'esempio di OP.

Con questo, addMsgpuò contrassegnati come (debolmente) pure, poiché solo la variabile locale this.msg è modificato:

class TestPure 
{ 
    string[] msg; 
    pure void addMsg(string s) 
    { 
     msg ~= s; 
    } 
}; 

e, naturalmente, ora è possibile utilizzare il (forte) pure funzione run2 senza modifiche.

pure TestPure run2() 
{ 
    TestPure t = new TestPure(); 
    t.addMsg("Test"); 
    t.addMsg("this."); 
    return t; 
} 
+1

Molto interessante. Strano come non ci sia un buon modo per gestire le risposte che non sono aggiornate nello stack overflow. –

+0

wait, le funzioni debolmente pure possono modificare le variabili di istanza di classe? In questo caso, non è possibile uscire dall'ambito della funzione "addMsg" e quindi non essere modificato? Se questo è possibile, allora cosa c'è di sbagliato nello scrivere una funzione pura o delegare all'interno di un'altra funzione, che modifica lo stato di quella funzione? –

+0

@Andrew: non fuori portata. Esiste un parametro implicito 'this' nelle funzioni membro e' msg' è raggiungibile da 'this'. – kennytm

3

Si prega di rivedere la definizione di funzioni pure:

funzioni pure sono funzioni che producono lo stesso risultato per gli stessi argomenti. A tal fine, una funzione di pura:

  • ha dei parametri che sono tutti invarianti o che sono implicitamente convertibili in invariante
  • non legge o scrivere qualsiasi stato mutevole globale

Uno degli effetti di usare le funzioni pure è che possono essere parallelizzate in sicurezza. Tuttavia, non è sicuro eseguire più istanze della funzione in parallelo, poiché potrebbero modificare contemporaneamente l'istanza della classe, causando un problema di sincronizzazione.

+0

"potevano entrambi modificare contemporaneamente l'istanza della classe" .. cosa ?? Come? – hasen

+0

auto tp = new TestPure; void threadFunc() {tp.addMsg ("Hello world"!); } nuova discussione (& threadFunc); nuova discussione (& threadFunc); È impossibile garantire che i due thread in esecuzione threadFunc non tentino di scrivere contemporaneamente sull'istanza tp. Pertanto, addMsg non può essere puro. –

+0

Ugh ... Sopra il commento eccetto che non sono stati combinati: http://dump.thecybershadow.net/ca761137c80da1cee3f2657b215f758d/00000039.txt –

-1

Solo un sospetto, ma questa funzione non restituisce sempre lo stesso risultato.

Vedere, restituisce un riferimento a qualche oggetto e mentre l'oggetto conterrà sempre gli stessi dati, gli oggetti restituiti da più chiamate alle stesse funzioni non sono identici; cioè, non hanno lo stesso indirizzo di memoria.

Quando si restituisce un riferimento all'oggetto, si restituisce essenzialmente un indirizzo di memoria, che sarà diverso tra più chiamate.

Un altro modo di pensarci, parte del valore di ritorno è l'indirizzo di memoria di un oggetto, che dipende da alcuni stati globali, e se l'output di una funzione dipende dallo stato globale, quindi non è puro. Diavolo, non deve nemmeno dipendere da ciò; finché una funzione legge uno stato globale, allora non è pura. Chiamando "nuovo", stai leggendo stato globale.

+0

La logica implica che le funzioni pure dovrebbero essere in grado di restituire solo i tipi di valori che possono entrare nei registri del processore ... –

+0

Quanto segue compila e mostra che vengono restituiti oggetti diversi per ogni chiamata. pure int * test_pure2() { int * p = new int; * p = 42; ritorno p; } int * i1 = test_pure2(); int * i2 = test_pure2(); writeln (i1, i2); –

+0

La funzione pura restituirà istanze diverse dello stesso valore. È ovvio che i puntatori non possono essere identici per le due istanze. Penso che sia chiaro cosa si intende qui - non si suppone che tu debba utilizzare gli indirizzi dei risultati (altrimenti - vedi il mio commento sopra). –

0

I pensare che il codice sia concettualmente corretto.Comunque potresti aver trovato casi in cui l'analisi semantica del compilatore non è buona come quella del tuo cervello.

Considerare il caso in cui la fonte della classe non è disponibile. In questi casi il compilatore non avrebbe modo di dire che addMsg modifica solo la variabile membro in modo che non possa permetterti di chiamarla da una funzione pura.

Per consentire nel tuo caso, dovrebbe avere una gestione caso speciale per questo tipo di utilizzo. Ogni regola di caso speciale aggiunta rende la lingua più complicata (o, se non documentata, la rende meno portabile)

4

Altri hanno già sottolineato che addMsg non è puro e non può essere puro perché muta lo stato dell'oggetto.

L'unico modo per renderlo puro è incapsulare le modifiche che stai apportando. Il modo più semplice per farlo è tramite la mutazione del ritorno e ci sono due modi per implementarlo.

In primo luogo, si potrebbe fare così:

class TestPure 
{ 
    string[] msg; 
    pure TestPure addMsg(string s) 
    { 
     auto r = new TestPure; 
     r.msg = this.msg.dup; 
     r.msg ~= s; 
     return r; 
    } 
} 

È necessario copiare la matrice precedente, perché all'interno di una funzione pura, il riferimento this è in realtà const. Nota che potresti fare meglio la copia allocando un nuovo array della dimensione finale e poi copiando gli elementi in te stesso. Si potrebbe utilizzare questa funzione in questo modo:

pure TestPure run3() 
{ 
    auto t = new TestPure; 
    t = t.addMsg("Test"); 
    t = t.addMsg("this."); 
    return t; 
} 

In questo modo, la mutazione si limita a ciascuna funzione pura con i cambiamenti passati tramite valori di ritorno.

Un modo alternativo di scrivere TestPure sarebbe quello di rendere i membri const e fare tutto la mutazione prima di passarlo al costruttore:

class TestPure 
{ 
    const(string[]) msg; 
    this() 
    { 
     msg = null; 
    } 
    this(const(string[]) msg) 
    { 
     this.msg = msg; 
    } 
    pure TestPure addMsg(string s) 
    { 
     return new TestPure(this.msg ~ s); 
    } 
} 

Speranza che aiuta.

+0

Immagino che questo sia probabilmente il meglio che ottiene. Speravo solo che potessimo evitare di generare tutta la spazzatura e il tempo speso a fare copie in qualche modo. –

+2

È divertente che tu dica che: la maggior parte delle lingue funzionali genera enormi quantità di spazzatura per esattamente le stesse ragioni. È per questo che i loro spazzini sono così efficienti. Questo non è qualcosa che è facilmente risolvibile. Se hai bisogno di mutare, non usare puro. –

+0

BTW, questo non è più vero. – BCS