2015-08-19 15 views
13

Date due liste, [a, b] e [c, d], mi piacerebbe ottenere il seguente risultato:Tutte le combinazioni di elementi di due liste a Haskell

[(a,c), (a,d), (b,c), (b,d)] 

Come posso fare questo in Haskell? Esiste una funzione integrata per questo, o dovrei implementarne una anch'io?

+3

Quando i tipi di '[a, b]' e '[c, d]' sono uguali, è possibile scrivere 'sequence [[a, b], [c, d]]'. – user3237465

risposta

21
[ (x,y) | x<-[a,b], y<-[c,d] ] 

Questo in realtà non richiede ulteriori spiegazioni, vero?

+0

No, non è vero, grazie; Mi stavo chiedendo se ci fosse un builtin che fa questo; qualcosa come 'combos xs ys' che produce lo stesso risultato. – Ben

+10

@Ben: 'combos = liftA2 (,)', che è sostanzialmente equivalente al suggerimento di Jubobs. – leftaroundabout

+0

@leftaroundabout uso molto piacevole di applicativi! –

5

uso di lista:

s = [a,b] 
s' = [c,d] 

all_combinations = [(x,y) | x <- s, y <- s'] 
8

come si può fare questo in un pseudocodice imperativo?

for each element x in [a,b]: 
    for each element y in [c,d]: 
     produce (x,y) 

In Haskell, questo è scritto come

outerProduct xs ys = 
    do 
     x <- xs   -- for each x drawn from xs: 
     y <- ys   -- for each y drawn from ys: 
     return (x,y)  --  produce the (x,y) pair 

(commenti di leftaroundabout) questo è naturalmente estremamente vicino a come liftM2 combinatore monadica è definita, quindi in fact

outerProduct = liftM2 (,) 

che è lo stesso di liftA2 (,) e le sue varie riscritture in termini di comprensione delle liste, funzione concatMap, >>=, <$> e <*>.

Concettualmente se questa è la roba del Applicative – che sarebbe meglio chiamato come Pairing, – perché questo abbinamento di elementi di due "contenitori" ⁄ "vettori" ⁄ qualunque è esattamente quello applicativo Functor è di circa . Accade semplicemente che la notazione do di Haskell funzioni per le monade e non (ancora) for applicatives.

in un certo senso di compilazione cicli annidati sono applicativo ⁄ funtori Abbinamento; Le Monade aggiungono la possibilità di creare loop annidati al volo, a seconda dei valori prodotti da un'enumerazione "esterna".

+4

Vale la pena notare che la notazione 'do'-notazione e lista è in realtà sia zucchero sintattico per gli stessi combinatori monadici, vale a dire' [a, b] >> = \ x -> [c, d] >> = \ y - > return (x, y) '. – leftaroundabout

+0

@leftaroundabout, semplicemente non è vero. La comprensione delle liste non ha uno ma due trattamenti nel desugarer, nessuno dei quali produce '>> =' o 'return'. – dfeuer

+0

Infatti; è necessario abilitare '-XMonadComprehensions' in modo che le intese delle liste effettivamente usino' >> = 'e' return'. Senza questa estensione, usano le funzioni specifiche dell'elenco equivalenti. – leftaroundabout

14

Stile applicativo fino in fondo!

λ> :m + Control.Applicative 
λ> (,) <$> ['a','b'] <*> ['c','d'] 
[('a','c'),('a','d'),('b','c'),('b','d')] 

(ho evitato di qualsiasi String zucchero sintattico sopra, al fine di rimanere vicino al tuo esempio.)

Per informazioni, (,) è sintassi speciale per una funzione che prende due argomenti e fa un paio di loro:

λ> :t (,) 
(,) :: a -> b -> (a, b) 

Edit: Come notato da leftaroundabout in his comment, è anche possibile utilizzare liftA2:

λ> :m + Control.Applicative 
λ> let combine = liftA2 (,) 
λ> combine "ab" "cd" 
[('a','c'),('a','d'),('b','c'),('b','d')] 
7

Il più inuitive sarebbe utilizzando di lista, altri aproaches includono l'utilizzo Applicat ive functors:

(,) <$> [1,2,3] <*> [4,5,6] 

Quindi, che cosa fa?

Ricorda che (,) :: a -> b -> (a, b) Accetta due argomenti e restituisce una tupla.

<$> è acuitally fmap, (<$>) :: Functor f => (a -> b) -> f a -> f b Prende una funzione e la solleva. In questo caso ci vuole (,) e sollevarlo per funzionare sulla lista. Quindi let x = (,) <$> [1,2] genererebbe x :: [b -> (Integer, b)] che è l'elenco di funzioni che prende b e restituisce tupla con un argomento fisso (Integer, b). Infine lo applichiamo usando <*> per generare tutte le combinazioni.