2011-06-11 14 views

risposta

1

Forse con una monade State combinata con modificatori e accessori personalizzati?

+0

D'accordo, si presenta come un uso particolare della State' Monade 'anche a me. –

-3

Non penso che ci sia un equivalente funzionale puro per il modello di stato. Perché la pura programmazione funzionale non ha il concetto di stato e tempo. Lo schema di stato riguarda intrinsecamente lo stato e il tempo. Ma penso che esista un equivalente funzionale non puro, è un flusso infinito e pigro valutato. Puoi implementarlo con resa C#.

+1

FP ha effettivamente stato. Mai sentito parlare di monadi? C'è persino qualcosa chiamato programmazione reattiva funzionale per catturare lo stato che varia nel tempo. – fuz

+0

per favore nota che ho usato le parole "puro" e "non puro" – Dagang

+1

Non credo che "abbia" sia la parola giusta per connettere "programmazione funzionale" e "stato". Mi piace pensarlo come "modello": possiamo fare calcoli statali costruendo un modello funzionale di ciò che significa lo stato. – luqui

7

Questo modello è un esempio dell'uso dello State monad, un ambiente computazionale che aumenta il codice con stato.

Ecco un'implementazione in Haskell.

Alcuni aiutanti:

import Control.Monad.Trans.State 
import Control.Monad.IO.Class 
import Data.Char 

le due modalità di funzionamento del programma

data Mode = A | B 

Il tipo di computazioni stateful con questa modalità, aumentata con un contatore.

type StateM a = StateT (Int, Mode) IO a 

La funzione di scrittura, una funzione nel contesto econom, cambia il suo comportamento in base alla modalità stateful:

writeName :: String -> StateM() 
writeName s = do 
    (n,mode) <- get 
    case mode of 
     A -> do liftIO (putStrLn (map toLower s)) 
       put (0,B) 
     B -> do let n' = n + 1 
       liftIO (putStrLn (map toUpper s)) 
       if n' > 1 then put (n', A) 
          else put (n', B) 

Eseguire il programma, lanciando un calcolo stateful inizialmente nello Stato A

main = flip runStateT (0, A) $ do 
    writeName "Monday" 
    writeName "Tuesday" 
    writeName "Wednesday" 
    writeName "Thursday" 
    writeName "Saturday" 
    writeName "Sunday" 

dal codice precedente, l'uscita principale è:

monday 
TUESDAY 
WEDNESDAY 
thursday 
SATURDAY 
SUNDAY 

Si noti che questa è una soluzione puramente funzionale. Non ci sono aggiornamenti mutevoli o distruttivi in ​​questo programma. Invece, la monade di stato filtra la modalità desiderata attraverso il calcolo.

+1

Non direi che questa è una codifica fedele: qui dobbiamo dichiarare tutte le modalità in un unico posto ('modalità dati'), mentre nell'esempio wikipedia le dichiarazioni possono essere combinate in modo modulare. – luqui

+0

Questa è la traduzione standard in tipi di dati chiusi in linguaggi in stile ML. Ovviamente, potremmo fare un uso di tipi di dati aperti tramite funzioni o classi di tipi, ma penso che non sia rilevante per questo problema. –

+1

Sono curioso di cosa si tratti di una traduzione standard e di cosa intendi per standard. – luqui

5

una codifica:

import Data.Char (toUpper, toLower) 

newtype State = State { unState :: String -> IO State } 

stateA :: State 
stateA = State $ \name -> do 
    putStrLn (map toLower name) 
    return stateB 

stateB :: State 
stateB = go 2 
    where 
    go 0 = stateA 
    go n = State $ \name -> do 
       putStrLn (map toUpper name) 
       return $ go (n-1) 

Non lasciatevi ingannare dal IO, questa è una pura traduzione di quel modello (non stiamo usando un IORef per memorizzare lo stato o qualsiasi cosa). Ampliare il newtype, vediamo ciò che questo tipo significa:

State = String -> IO (String -> IO (String -> IO (String -> ... 

Si prende una stringa, fa qualche I/O e chiede un'altra stringa, ecc

Questo è il mio preferito di codifica dei modelli di classe astratta a OO: classe astratta -> tipo, sottoclassi -> elementi di quel tipo.

La dichiarazione newtype State sostituisce la dichiarazione astratta writeName e la sua firma. Invece di passare uno StateContext in cui assegniamo un nuovo stato, abbiamo semplicemente restituito il nuovo stato.L'incorporamento del valore restituito in IO dice che il nuovo stato può dipendere dall'I/O. Dal momento che non è tecnicamente necessario in questo esempio, si potrebbe utilizzare il tipo più spinto

newtype State = State { unState :: String -> (State, IO()) } 

in cui possiamo ancora esprimere questo calcolo, ma la sequenza di stati è fisso e non è permesso dipendere ingresso. Ma atteniamoci al tipo originale, più indulgente.

E per il "client di prova":

runState :: State -> [String] -> IO() 
runState s [] = return() 
runState s (x:xs) = do 
    s' <- unState s x 
    runState s' xs 

testClientState :: IO() 
testClientState = runState stateA 
        [ "Monday" 
        , "Tuesday" 
        , "Wednesday" 
        , "Thursday" 
        , "Saturday" 
        , "Sunday" ] 
+0

P.S. Avrai bisogno di catturare la semantica di commutazione contatore pure. –

+0

@Don, oh, giusto. hmm .... – luqui

+0

Per quello che vale, direi che questo stile di codifica è ideale solo per le vere classi idiomatiche OO che incapsulano comportamenti astratti. Un deprimente numero di "classi" nel presunto codice OO è molto più vicino alle traduzioni maldestre dello stile usato da Don e quindi beneficiano di essere tornati in quello che volevano essere in primo luogo ... –

Problemi correlati