Вопрос

I'm new to haskell and I am trying to learn haskell. I am trying to create simple functions like "duplicate" in the most possible ways i can think. I have already done "duplicate" with pattern matching and guards. I can't get it working with a let inside a list comprehension. I can imagine let inside a list comprehension is not ideally but I am more interested why it does not work =].

My question is: why does duplicate' produces [Int] and duplicate'' produces [[Int]] and is it even possible to produce [Int] with a let inside a list comprehension?

Thank you for your time and help :).

--list comprehension
duplicate' xs = [y | x <- xs, y <- [x,x]]
input => [1,2,3,4]
output => [1,1,2,2,3,3,4,4]
expected? => yes

--list comprehension with let
duplicate'' xs = [y | x <- xs, let y = [x,x]]
input => [1,2,3,4]
output => [[1,1],[2,2],[3,3],[4,4]]
expected? => yes
Это было полезно?

Решение

<- and let simply mean different things.

When you write y <- [x,x], you are saying "give y each of the values inside the list [x,x] in turn".

When you write let y = [x,x], you are saying "give y the value [x,x]".

Другие советы

let does nothing but define a new symbol to take one some given value. You could always just inline the definition by hand:

[ y | x <- xs, let y = [x,x] ] ≡ [ [x,x] | x <- xs ]

Any such expression with only one <- has thus the form

[ f x | x<-xs ]

which is equivalent to map f xs. So the result list must then always have the same length as xs, making the desired behaviour of duplicate impossible to achieve: if you want to have duplicates, you need to encapsulate them as inner lists so they won't count as more elements.

To merge these nested lists back into a "flat" one, you can utilise the fact that lists are a monad:

join :: Monad m => m (m a) -> m a

Prelude Control.Monad> join [[1,1], [2,2]]
[1,1,2,2]

Now, join is in fact how category theorists prefer to define monads, but as you may know Haskell does it a bit differently:

(>>=) :: Monad m => m a -> (a -> m b) -> m b
a >>= f = join (fmap f a)

or, as it is actually defined the other way around,

join a = a >>= id

Put that into the modified let version of duplicate:

join [y | x <- xs, let y = [x,x] ]
  ≡ [y | x <- xs, let y = [x,x] ] >>= id
  ≡ map (\x -> [x,x]) xs >>= id
  ≡ xs >>= id . (\x -> [x,x])
  ≡ xs >>= (\x -> [x,x])
  ≡ do { x<-xs; [x,x] }
  ≡ do { x<-xs; y<-[x,x]; return y }  -- by the monad laws

Now, an expression do { a<-p; b<-q; ... return x } is a monad comprehension, the generalisation of list comprehensions. It can be rewritten [x | a<-q, b<-q, ...]. For our problem,

join [y | x <- xs, let y = [x,x] ] ≡ [y | x<-xs, y<-[x,x]]

which is where you started. It is inevitable to use two <-s when doing this with a pure list comprehension.

Of course you could still use also a let, at any point...

[y | x<-xs, y<-[x,x]]
     ≡ [y | x<-xs, let z=[x,x], y<-z]
     ≡ [a | x<-xs, let z=[x,x], y<-z, let a=y]
     ≡ [a | x<-xs, let z=let w=[let q=x in q, let r=x in r] in w, y<-z, let a=y]
     ≡ ...
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top