2015-08-19 3 views
13

zwei Listen gegeben, [a, b] und [c, d], würde Ich mag das folgende Ergebnis erhalten:Alle Kombinationen von Elementen aus zwei Listen in Haskell

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

Wie kann ich dies tun in Haskell? Gibt es dafür eine eingebaute Funktion oder sollte ich selbst eine implementieren?

+3

Wenn die Typen von '[a, b]' und '[c, d]' gleich sind, können Sie 'sequence [[a, b], [c, d]]' 'schreiben. – user3237465

Antwort

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

Dies erfordert keine weitere Erklärung, oder?

+0

Nein, tut es nicht wirklich, danke; Ich habe mich nur gefragt, ob da ein eingebauter ist, der das macht; etwas wie "Combos xs ys", das das gleiche Ergebnis produziert. – Ben

+10

@Ben: 'combos = liftA2 (,)', was im Grunde dem Vorschlag von Jubob entspricht. – leftaroundabout

+0

@leftaroundabout sehr nette Verwendung von Applicatives! –

5

Verwendung Listenkomprehension:

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

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

Wie können Sie dies tun in einer imperativen Pseudo-Code?

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

In Haskell, wird dies geschrieben als

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 

(folgende Kommentare von leftaroundabout) dies natürlich extrem dicht an wie liftM2 monadischen combinator definiert ist, so in Tatsache

outerProduct = liftM2 (,) 

, das ist das gleiche wie liftA2 (,), und seine verschiedenen schreibt in Bezug auf Listenerfassungen, concatMap Funktion, , <$> und <*> Betreiber.

Konzeptionell obwohl dies ist der Stoff, der Applicative –, die besser als Pairing genannt werden würde, – weil diese Paarung der Elemente von zwei „Container“ ⁄ „Träger“ ⁄ was ist genau das, was Applicative Functor im Begriff ist, . Es passiert einfach, dass Haskells do Notation für Monaden funktioniert, und nicht (noch) for applicatives.

In gewissem Sinne Kompilierung- verschachtelten Schleifen Applicative ⁄ Pairing functors sind; Monaden fügen die Fähigkeit hinzu, verschachtelte Schleifen spontan zu erstellen, abhängig von den Werten, die von einer "äußeren" Enumeration erzeugt werden.

+4

Es ist erwähnenswert, dass 'do'-Notation und List Comprehensions eigentlich beide syntaktischer Zucker für die gleichen monadischen Kombinatoren sind, nämlich' [a, b] >> = \ x -> [c, d] >> = \ y - > Rückkehr (x, y) '. – leftaroundabout

+0

@leftaroundabout, das ist einfach nicht wahr. Listenkompromittierungen haben nicht eine, sondern zwei Behandlungen im Desugarer, von denen keine '>> =' oder 'return 'erzeugt. – dfeuer

+0

In der Tat; man muss '-XMonadComprehensions' aktivieren, damit die Listenkomprehensionen tatsächlich' >> = 'und' return' verwenden. Ohne diese Erweiterung verwenden sie die entsprechenden listenspezifischen Funktionen. – leftaroundabout

14

Applicative Stil den ganzen Weg!

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

(Ich habe jeden String syntaktischen Zucker oben gemieden, um Ihr Beispiel nahe zu bleiben.)

Informationen, (,) ist eine spezielle Syntax für eine Funktion, die aus ihnen zwei Argumente und macht ein Paar nimmt:

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

Edit:, Wie bereits erwähnt in his comment von leftaroundabout können Sie auch liftA2:

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

Die Inuitive Verständnis Liste mit würde, auch andere aproaches applicat mit ive Funktoren:

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

Also was macht das?

Denken Sie daran, dass (,) :: a -> b -> (a, b) zwei Argumente akzeptiert und ein Tupel zurückgibt.

<$> ist eigentlich fmap, (<$>) :: Functor f => (a -> b) -> f a -> f b Es braucht eine Funktion und heben sie an. In diesem Fall dauert es (,) und heben Sie es zur Arbeit auf der Liste. So let x = (,) <$> [1,2] würde x :: [b -> (Integer, b)] generieren, die die Liste der Funktionen ist, die b dauert und Tupel mit einem festen Argument (Integer, b) zurückgibt. Schließlich wenden wir es unter Verwendung <*> an, um alle Kombinationen zu erzeugen.