2014-05-07 10 views
6

Conosco un po 'di Prolog e ho appena iniziato un po' di studio individuale in Haskell. Ho lavorato attraverso lo 99 Problems for Haskell, imparando molto lungo la strada e godendo davvero Haskell. A volte cerco di chiarire la mia comprensione di uno spazio problematico codificando qualcosa in Prolog e poi pensando a come quella soluzione potrebbe riguardare un approccio funzionale in Haskell. Stasera, mentre lavoravo sui problemi relativi alla logica (problemi 47 e 48), mi sono distratto nel tentativo di realizzare il semplice compito di creare un elenco degli elenchi di tutte le combinazioni di valori di verità con n valori. Voglio una funzione tValues :: Int -> [[Bool]]. Questo problema può essere risolto in modo molto semplice e dichiarativa in Prolog, es .:Pensare al Prolog e ad Haskell - Generare liste di combinazioni di valori di verità

combinations_of_n_truth_values(N, AllValues) :- 
    findall(Values, n_truth_values(N, Values), AllValues). 

n_truth_values(N, TruthValues) :- 
    length(TruthValues, N), 
    maplist(truth_value, TruthValues). 

truth_value(true). 
truth_value(false). 

Quando usato, la variabile AllValues sarà unificare con l'elenco desiderato di elenchi di valori di verità. Mi piacerebbe sapere come un esperto programmatore Haskell dovrebbe risolvere questo stesso problema. Mi aspetto che ci sia una soluzione altrettanto semplice e dichiarativa, ma non riesco a far funzionare il mio cervello di Haskell nel modo giusto.

ho giocherellava con alcuni semi-analoghi utilizzando list comprehension, come ad esempio il seguente:

tValues:: Int -> [[Bool]] 
tValues 0 = [] 
tValues n = [v:vs | v <- tValue 
        , vs <- tValues (n-1) ] 

tValue :: [Bool] 
tValue = [True, False] 

ma tValues restituisce solo []. Penso di aver bisogno solo di un impegno da uomo a uomo per aiutare a scuotere la mia mente in modo chiaro e magari a darmi una visione più profonda.

Molte grazie.

risposta

9

In pseudo-codice, la lista la vostra comprensione

[v:vs | v <- tValue 
     , vs <- tValues (n-1) ] 

pari

for any combination of two elements in `tValue` and `tValues (n-1)` 
    cons the first onto the latter 

Tuttavia, tValues non ci sono elementi per cominciare, si tratta di una lista vuota. Consente di simulare questo per n = 1:

tValues 1 = [v:vs | v <- tValue 
        , vs <- tValues 0 ] 
      = [v:vs | v <- [True, False] 
        , vs <- [] ] 
      -- since there is no vs 
      = [] 

Questo propaga in tutta la ricorsione. La soluzione è molto semplice: cambiare il caso base per contenere una combinazione di vuoto:

tValues 0 = [[]] -- or: return [] 

Ora i rendimenti della simulazione:

tValues 1 = [v:vs | v <- tValue 
        , vs <- tValues 0 ] 
      = [v:vs | v <- [True, False] 
        , vs <- [[]]] 
      -- vs is [] 
      = [True:[],False:[]] 
      = [[True],[False]] 

che è proprio quello che volevamo.

+0

Grazie per la risposta! Penso che questo chiarisca alcuni problemi che stavo riscontrando quando cercavo di lavorare con le list comprehensions. È anche incoraggiante sapere che stavo trascurando un dettaglio minore, piuttosto che fondamentalmente malinteso sulla questione. Apprezzo anche la traduzione in linguaggio naturale, poiché i miei studi in Prolog mi hanno abituato a formulare risposte in semplici dichiarazioni dichiarative e costruire algoritmi su questa base. –

+0

Sareste interessati a valutare la relazione tra questo approccio e gli altri offerti qui? Ad es., C'è una differenza ideologica che motiva l'uso esplicito della lista monad in contrasto con la sintassi list-builder, o è semplicemente una questione di stile/convenienza? In ogni caso, grazie ancora! –

+1

@aBathologist: mi definisco ancora un principiante Haskell, quindi il mio punto di vista probabilmente non è realmente professionale. Per me, è più o meno una questione di convenienza: la sintassi di comprensione delle liste è facile da leggere e può essere compresa dai principianti di Haskell. Può anche essere immediatamente tradotto in una forma monadica: 'do {v <- [True, False]; vs <- tValues ​​(n-1); return (v: vs);} '(i predicati possono essere fatti tramite' guard' da 'Control.Monad'). Questa versione intermedia monadica può aiutarti a trovare quelli eleganti come "flip replicateM [True, False] di m09". Alla fine tocca a te. – Zeta

4
truths :: [[Bool]] 
truths = [True] : [False] : concatMap (\bs -> [True:bs, False:bs]) truths 

truthValues :: Int -> [[Bool]] 
truthValues n = dropWhile ((< n) . length) . takeWhile ((<= n) . length) $ truths 

truths è un elenco infinito di tutte le combo valore di verità, perché le lunghezze aumentano monotonicamente possiamo solo prendere la parte anteriore della lista, mentre le lunghezze sono inferiori o uguali ai set che stiamo cercando, quindi rilasciare la parte anteriore di quelle le cui lunghezze sono inferiori.

Il motivo per cui si sta recuperando solo la lista vuota è che alla fine tValues (n-1) restituisce tValues 0 che è ovviamente la lista vuota. Provare a disegnare dalla lista vuota in una lista di comprensione fa fallire l'iterazione. Questo diffonde la catena di comprensione. Cambiare il caso base su tValues 1 = [[True], [False]] e funzionerà.

+0

Grazie mille per la vostra soluzione interessante e per aver dato una diagnosi così comprensibile del problema con la mia condizione di base. Le altre risposte non hanno evidenziato il fatto che 'v <- []' è una condizione in errore, e la comprensione di questo mi aiuta davvero a chiarire il problema. Mi piace molto questo approccio, che genera un oggetto astratto costituito da tutte le possibili serie di valori di verità. Faccio un balbettamento un po 'per l'overhead di aver tagliato gli insiemi desiderati dal centro della lista infinita. Ma questo potrebbe essere più un problema psicologico che uno computazionale? Grazie molto! –

+2

Felice di aver trovato valore! Hai ragione, però, questa non è sicuramente la soluzione più efficiente che è stata data qui. È soprattutto una novità intelligente che usa lo stesso schema della mia definizione preferita della sequenza di Fibonacci, 'fibs = 1: 1: zipWith (+) fib (fib di coda)' – bisserlis

4

Non una risposta completa per sé, ma se vi interessa, con l'elenco monade:

truthValues ∷ Int → [[Bool]] 
truthValues 0 = return [] 
truthValues n = truthValues (n - 1) >>= (\l → [True:l, False:l]) 

o

truthValues n = foldM (\l _ -> [True:l, False:l]) [] [1..n] 

o

truthValues = flip replicateM [True, False] 

Vedi anche Will Ness' risposta e i commenti in allegato :)

+0

Grazie mille per le risposte. Il mio piano pomeridiano ruota intorno allo studio delle soluzioni per capire chiaramente cosa sta succedendo. Le tue risposte di lista-monade mi interessano davvero: sarei particolarmente interessato a conoscere un po 'la motivazione per lavorare con la lista monad direttamente invece di usare solo la sintassi list-builder. Ci sono casi d'uso particolari che ti portano in un modo o nell'altro o è più una questione di principio? –

+0

Giudicando superficialmente, sembra che lavorare con la monade porti a uno stile più imperativo di ciò che con il binding e 'return' e' flip' ecc.). È una caratteristica comune lavorare direttamente con le monadi o sto forse fraintendendo qui? Grazie molto! –

+1

'return' non è di natura imperativa.Non è la stessa cosa che il ritorno in un linguaggio imperativo: mette solo il valore che ha dato alla monade. Qui 'return []' potrebbe essere stato scritto '[[]]' pure. 'flip' è stato appena usato qui per ottenere uno stile point-free, potremmo aver scritto' truthValues ​​n = replicateM n [True, False] ' invece. Qui la cosa bella della lista monade è che il suo nucleo - la sua operazione 'bind' --- fa esattamente quello che vuoi: cioè combina due valori in modo non deterministico (a la Prolog). Ecco perché l'ultima soluzione è così breve e precisa. – m09

6

Con la lista Monade,

g n = mapM (\_-> [True, False]) [1..n] 

Chi caso base della vostra funzione. Poiché la funzione restituisce un elenco contenente di valori di verità di lunghezza n, ne consegue che per n=0 deve restituire un elenco contenente tutti gli elenchi di valori di verità di lunghezza 0, ovvero un elenco contenente una lista vuota.

+1

davvero molto bello, meglio della mia versione, +1 :) – m09

+3

e per esplicitare la parte che "usa la monade", potremmo scriverla 'sequenza. replicare n $ [True, False] ' – m09

+2

@ m09 anche 'M' nella' mapM' mostra questo. :) –

Problemi correlati