Domanda

sulla programmazione funzionale, e ha ricevuto (bene!) le risposte che hanno spinto più domande (come sembra essere il caso con l'apprendimento, a volte). Qui ci sono un paio di esempi:

  1. Una risposta fatto riferimento ad un vantaggio di strutture di dati immutabili: ogni thread può avere la propria copia. Ora, a me, questo suona un po 'come un sistema di controllo di versione (per usare un'analogia), dove invece di codice che qualcuno ha verificato in modo che non può essere modificato da nessun altro blocco, tutti possono verificare le loro proprie copie. Suona bene. Tuttavia, in VCS si ha il concetto di "fondere" le modifiche, nel caso in cui due persone hanno cambiato le stesse cose. Sembra che questo problema potrebbe certamente venire in uno scenario multithread ... così come è "la fusione" fatto quando è importante che le discussioni vedere i dati più recenti?

  2. Questa risposta ha parlato del caso in cui le operazioni venivano eseguita in un ciclo su un oggetto, e come è possibile utilizzare un nuovo oggetto ogni volta attraverso invece di aggiornare una vecchia. Tuttavia, diciamo che il bankAccount è in corso di aggiornamento in uno scenario di non-loop - ad esempio un sistema GUI bancario. L'operatore fa clic sul pulsante "Modifica Tasso di Interesse", che genera un evento che avrebbe (in C # per esempio) fare qualcosa di simile bankAccount.InterestRate = newRateFromUser. Mi sento come se fossi di essere densa qui, ma spero che il mio esempio ha un senso: ci deve essere qualche modo che l'oggetto viene aggiornato, giusto? Diverse altre cose possono dipendere dai nuovi dati.

In ogni caso, se si può aiutarmi a ottenere la mia testa intorno al cambiamento di paradigma, sarei riconoscente. Mi ricordo che il mio cervello sta attraversando simili "fasi stupidi" quando si impara OOP dopo uno sfondo di approccio semplice imperativo procedurale alla codifica.

È stato utile?

Soluzione

Risposta alla parte 1: oggetti immutabili non lo fanno da soli sostenere una cosa del genere "fusione" per consentire i risultati di due aggiornamenti del filo da combinare. Ci sono due strategie principali per questo: il pessimista e l'ottimista. Se sei pessimista, si assume che sia molto probabile che due thread vorranno aggiornare lo stesso pezzo di dati allo stesso tempo. Così si impiegano blocco, in modo tale che il secondo thread si blocca fino a quando il primo thread dice che ha finito. Se siete ottimisti che questo accadrà solo raramente, si lascia che entrambi i fili lavorare sulla propria copia logica dei dati. Quello che finisce prima fornisce la nuova versione, e l'altro ha di ricominciare dall'inizio - solo ora inizia dai risultati delle modifiche del primo filo. Questo riavvio costoso accade solo occasionalmente però, in modo oltre tutto funziona meglio a causa della mancanza di bloccaggio (anche se questo è vero solo se il vostro ottimismo è stato ben posizionato su come si verifica raramente uno scontro).

Parte 2: Pure lingue senza stato funzionali in realtà non eliminano il problema. Anche un programma di Haskell puro può avere stato associato con esso. La differenza è che il codice stateful ha un tipo diverso di ritorno. Una funzione che manipola stato è espresso come una sequenza di operazioni che operano su un oggetto che rappresenta quello stato. In un esempio assurdo, in considerazione del file system del computer. Ogni volta che un programma modifica il contenuto di un file (anche da un solo byte) ha creato una nuova "versione" di tutto il file system. E per estensione, una nuova versione di tutto l'universo. Ma concentriamoci sul file system, per ora. Qualsiasi altra parte del programma che prende in esame il file system può ora essere influenzata da quel byte modificato. Così Haskell dice che le funzioni che operano sul file system devono passare in modo efficace attorno a un oggetto che rappresenta una versione del file system. Poi perché questo sarebbe noioso trattare manualmente, trasforma il requisito dentro e dice che se una funzione vuole essere in grado di fare IO, deve restituire una sorta di oggetto contenitore. All'interno del contenitore è il valore della funzione vuole tornare. Ma il contenitore serve come prova che la funzione sia anche ha effetti collaterali né può vedere gli effetti collaterali. Ciò significa che il sistema tipo Haskell è in grado di distinguere tra le funzioni-con-effetti collaterali e funzioni "puri". Così aiuta a contenere e gestire lo statefulness di codice, senza realmente eliminarlo.

Altri suggerimenti

Pensate alla classe String in Net (che è un oggetto immutabile). Se si chiama un metodo su una stringa, si ottiene una nuova copia:

String s1 = "there";
String s2 = s1.Insert(0, "hello ");

Console.Writeline("string 1: " + s1);
Console.Writeline("string 2: " + s2);

Questa uscita volontà:

stringa di 1: vi

string 2: ciao là

Confronto questo comportamento per StringBuilder, che ha fondamentalmente la stessa firma del metodo:

StringBuilder sb  = new StringBuilder("there");
StringBuilder sb2 = sb.Insert(0, "hi ");

Console.WriteLine("sb 1: " + sb.ToString());
Console.WriteLine("sb 2: " + sb2.ToString());

Poiché StringBuilder è mutevole, entrambe le variabili puntano allo stesso oggetto. L'output sarà:

sb 1: Hi there

sb 2: Hi there

Quindi, è assolutamente possibile non modificare una stringa una volta che hai creato. s1 sarà sempre "lì" fino alla fine del tempo (o fino suoi rifiuti raccolti). Questo è importante in threading, perché si può sempre scorrere ogni personaggio e stampare il suo valore sapendo che sarà sempre stampare 'lì'. Se hai iniziato a stampare lo StringBuilder dopo che è stato creato, è possibile stampare i primi due caratteri di lì e get'th'. Ora, immaginate un altro thread arriva inserti pubblicitari 'ciao'. Il valore è ora diverso! Quando si stampa il terzo personaggio, è lo spazio da 'ciao'. Così la stampa:. 'C'è th'

Per quanto riguarda la 2 # ...

  

Molte altre cose possono dipendere dal   i nuovi dati.

Questo è ciò che i puristi chiamano un "effetto". La nozione di molteplici riferimenti a oggetti allo stesso oggetto mutabile è l'essenza dello stato mutevole e il nocciolo della questione. In OOP, si può avere un oggetto "a" di tipo BankAccount, e se leggete a.Balance o roba del genere a diversi volte è possibile vedere diversi valori. Al contrario, in puro FP, se "a" è di tipo BankAccount, allora è immutabile e ha lo stesso valore indipendentemente dal tempo.

Tuttavia, poiché un BankAccount è presumibilmente un oggetto vogliamo modellare il cui stato non variano con il tempo, avremmo in FP codificare tali informazioni nel tipo. Quindi, "a" potrebbe avere tipo "IO BankAccount", o qualche altro tipo monade che si riduce essenzialmente a rendere "a" in realtà essere una funzione che prende come input "lo stato precedente del mondo" (o precedente stato di tassi di interesse bancari , o qualsiasi altra cosa), e restituisce un nuovo stato del mondo. Aggiornare il tasso di interesse sarebbe un'altra operazione con un tipo che rappresenta l'effetto (per esempio un'altra operazione IO), e pertanto restituirebbe una nuova 'mondo', e tutto ciò che può dipendere dal tasso di interesse (stato mondiale) sarebbe dati con tipo che sa che ha bisogno di prendere quel mondo come input.

Di conseguenza, l'unico modo possibile chiamare "a.Balance" o roba del genere è quello di utilizzare il codice che, grazie ai tipi statici, impone che un po 'la storia del mondo che ci ha portato fino ad ora' è stato correttamente scandagliato a il punto della chiamata, e tutto ciò che la storia del mondo è l'ingresso colpisce quale risultato ci arriveremo da a.Balance.

La lettura sul Stato Monade può essere utile per ottenere un senso di come si modella 'condiviso stato mutabile' puramente.

  1. strutture di dati immutabili non sono come VCS. Pensate a una struttura di dati Immutabile come un file di sola lettura. Se la sua sola lettura, non importa che sta leggendo, che parte del file in un dato momento, ognuno leggerà le informazioni corrette.

  2. La risposta sta parlando di http://en.wikipedia.org/ wiki / Monad_ (functional_programming)

MVCC ( multiversione Controllo della concorrenza )

La soluzione per il problema a cui ti riferisci è descritto da Rich Hickey nel suo il video presentazioni .

In breve : invece di passare i dati facendo riferimento direttamente ai clienti, si aggiunge un livello più indiretto e passare il riferimento al riferimento ai dati. ( Beh, in realtà si vorrebbe avere almeno un altro livello di indirezione. Ma supponiamo che la struttura dei dati è molto semplice, come la "matrice". )
Dal momento che i dati sono immutabili, ogni volta che i dati devono essere modificati, si crea la copia della parte modificata ( nel caso della matrice è necessario creare un altro array! ), più si crea un altro riferimento a tutte le "cambiato" i dati.
Quindi, per tutti i clienti che hanno utilizzato la prima versione della matrice, che utilizzano il riferimento alla prima versione. Ogni cliente tenta di accedere al 2 ° versione utilizza il secondo riferimento.
La struttura dei dati "matrice" non è molto interessante per questo metodo, perché non è possibile suddividere i dati e si è costretti a copiare tutto. Ma per le strutture dati più sofisticate come gli alberi, alcune parti della struttura dati possono essere "condivise", quindi non si è costretti a copiare tutto ogni volta.

Per ulteriori informazioni si prega di dare un'occhiata a questo articolo: " Strutture dati puramente funzionale " da Chris Okasaki.

"Immutabile" significa esattamente questo:. Non cambia

Il modo in cui i programmi funzionali fanno aggiornamenti è passando intorno alle cose nuove. Un valore esistente non cambia mai: basta costruire un nuovo valore e passa che, invece. Molto spesso il nuovo stato azioni di valore con quello vecchio; buoni esempi della tecnica sono liste composte da celle cons, e il cerniera .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top