Occuparsi di stati in un approccio immutabile [chiuso]

3

Voglio sapere come comportarmi quando hai alcuni stati in un programma, con funzioni che dipendono da loro, ma con un approccio immutabile. Ho letto alcuni esempi e domande, ma tutto si concentra su un piccolo scenario, voglio capirlo in un programma più grande, vicino al mondo reale.

La mia domanda non riguarda un linguaggio di programmazione o un paradigma (potrebbe essere fatto con Java per esempio, poiché non cambiamo i valori delle variabili), quindi illustrerò un semplice problema con questo pseudo codice:

--some states of my program
playerOne = {
  cards :: [Card]
  score :: Int
}
playerTwo = {
  cards :: [Card]
  score :: Int
}
tableCards :: [Card]

--a function that will perform a move in a game
--it needs to move a card from player to tableCards
--it needs to change the other player score with -1
doSomeMove()

--moves
doSomeMove(playerOne)
showScore() --p1: 10 p2: 9 tableCards: 1
doSomeMove(playerTwo)
showScore() --p1: 9 p2: 9 tableCards: 2
doSomeMove(playerOne)
showScore() --p1: 9 p2: 8 tableCards: 3
doSomeMove(playerTwo)
showScore() --p1: 8 p2: 8 tableCards: 4

Primo: in "doSomeMove ()" ho bisogno di leggere le carte di un giocatore. Dal momento che chiamerò questa funzione nel mezzo del gioco non posso leggere dalla variabile "playerOne", dato che è immutabile e non cambierà nel bel mezzo del gioco! Quindi, come posso risolvere questo? Per leggere gli stati attuali del programma attuale? devo ricevere sempre gli stati attuali delle carte giocatore per parametro?

Secondo: cambierò le carte (giocatore e tavolo) e gli altri giocatori in questa funzione. Dato che non posso davvero cambiare le variabili (dato che è immutabile) le clonerò e imposterò lo stato modificato in una variabile locale all'interno della funzione, giusto? Ma come può la prossima funzione (doSomeMove () o altri) conoscere questo cambiamento?

Fondamentalmente, l'unica cosa che posso pensare di risolvere è: ottenere l'intero stato del programma in base a parametri, apportare alcune modifiche e restituire tutto come parametro oltre all'utilizzo successivo della funzione e tenere conto delle modifiche di stato. Ma penso che questo sia molto poco pratico, dato che se ho 20 stati e 50 funzioni, ho bisogno di tutte e 50 le funzioni che passano e restituisco tutti e 20 gli stati ciascuno?

Non voglio risolvere questo problema, solo per capire come risolvere programmi come questo o più grandi con immutabilità. E spero che questa domanda aiuti un'altra persona, dal momento che ho chiesto ad alcune persone come risolvere questo problema e nessuno ha risposto sicuramente! Grazie!

    
posta Wagner 29.04.2015 - 05:24
fonte

2 risposte

1

Al livello più alto, puoi usare un fold per alimentare l'output dell'ultimo passo nell'input del passaggio successivo senza dovergli assegnare un nome intermedio che viene riassegnato. Fondamentalmente, in ogni fase crei un nuovo ambito. Questa parte è molto più semplice con la programmazione reattiva funzionale o gli attori.

Al di sotto di questo livello, l'intuizione chiave è dovuta all'immutabilità, le variabili di stato non devono sempre essere raggruppate insieme. Puoi decostruire il tuo stato in modo discendente, creando quante più copie o fette di cui hai bisogno, quindi lo ricostruisci al ritorno. In questo modo le funzioni che crei non devono essere a conoscenza dell'intero stato, solo ciò che viene passato come argomento.

Il tuo esempio di doSomeMove potrebbe contenere la mano di un giocatore, le carte tabella e il punteggio del giocatore avversario, e restituire nuovi valori per tutti quelli. Potrebbe essere implementato in questo modo:

doSomeMove(hand, table, score)
    (newHand, removedCard) = removeCard(hand)
    newTable = addCard(table, removedCard)
    return (newHand, newTable, decrementScore(score))

Quindi il codice che chiama doSomeMove prenderebbe in tutte le mani dei giocatori, e tutti i loro punteggi, decostruirlo per effettuare la chiamata, quindi ricostruirlo:

callingCode(hands, scores, table)
    (playerHand, opponentHand) = hands
    (playerScore, opponentScore) = scores
    (newPlayerHand, newTable, newOpponentScore) = doSomeMove(playerHand, table, opponentScore)
    return ((newPlayerHand, opponentHand), (playerScore, newOpponentScore), newTable)

Ci sono schemi che possono semplificare questo, e puoi dividerlo in più livelli di astrazione, ma il mio punto era mostrare l'approccio di base da prendere in modo che doSomeMove non abbia bisogno di essere consapevole dei tuoi intero stato , solo ciò che è rilevante. Più in basso nella pila delle chiamate, meno lo stato di cui hai bisogno. Ad esempio, removeCard non ha idea di chi sia stata rimossa la carta. Ci vuole un po 'di pratica per essere in grado di evitare di passare tutto il tuo stato dappertutto, ma è fattibile.

    
risposta data 30.04.2015 - 00:10
fonte
3

Per riflettere lo stato modificato di un oggetto (chiamerò questo oggetto ora e riferirò con questo al paradigma OOP) puoi restituire nuovamente questo oggetto che di per sé è di nuovo immutabile, ad esempio

playerOne = doSomeMove(playerOne)

Fondamentalmente questa è l'idea alla base del modello di stato .

Con questo il singolo oggetto è immutabile ma i tuoi cambiamenti si riflettono nella creazione di nuovi oggetti (che è anche il modo in cui un elenco immutabile funziona).

Se devi modificare due oggetti contemporaneamente, devi cercare come restituirli.

Penso che il tuo malinteso fondamentale sia quello di utilizzare variabili globali e considerarle immutabili (distinguere tra uno stato di oggetto immutabile e una variabile immutabile.

    
risposta data 29.04.2015 - 07:45
fonte

Leggi altre domande sui tag