How do I desugar
λ: [[b|(a,b)<-[(1,"A"),(2,"B")], mod x 2 == 0]|x <- [1..10]]
[[],["A","B"],[],["A","B"],[],["A","B"],[],["A","B"],[],["A","B"]]
I have tried
do
x <- [1..10]
do
(a,b) <- [(1,"A"),(2,"B")]
guard $ mod x 2 == 0
return b
but this seems to automatically join the result.
["A","B","A","B","A","B","A","B","A","B"]
Remember that nested do constructs "collapse":
do
A
do B
C
is the same as
do
A
B
C
(Or more precisely, multi-line do constructs desugar to nested do constructs.) So you want the sublist to be constructed and add to the outer list, rather than building a single list using the nested do construct to fill it.
Using a hybrid, intermediate approach, you build the outer list by returning each inner list separately.
do
x <- [1..10]
return [[b|(a,b)<-[(1,"A"),(2,"B")], mod x 2 == 0]
Then you desugar that comprehension:
do
x <- [1..10]
return (do
(a, b) <- ...
guard $ mod x 2 == 0
return b)
Since you return the list comprehension, you need an extra return:
do
x <- [1..10]
return $ do
(a,b) <- [(1,"A"),(2,"B")]
guard $ mod x 2 == 0
return b
Otherwise, you would construct have constructed it like:
[b|x <- [1..10], (a,b)<-[(1,"A"),(2,"B")], mod x 2 == 0]
Since you used the do as tailing element in the outer do, that do was redundant.
I made minor modifications to your original list comprehension.
[ (x,[(b) | (a,b) <- [(1,"A"),(2,"B")], mod x 2 == 0]) | x <- [1..10] ]
Which resulted in
[(1,[]),(2,["A","B"]),(3,[]),(4,["A","B"]),(5,[]),(6,["A","B"]),(7,[]),(8,["A","B"]),(9,[]),(10,["A","B"])]
So with these parameters, it was producing b of (a,b) or a [].
This translated into the following map.
map (\x -> if mod x 2 == 0 then [x] else [] ) [1..10]
Which produced
[[],[2],[],[4],[],[6],[],[8],[],[10]]
The following segment of your list comprehension
[b|(a,b)<-[(1,"A"),(2,"B")]]
Produces simply
["A","B"]
The following map produces the same.
map snd [(1,"A"),(2,"B")]
Replacing [x] in the first map with the preceding map
map (\x -> if mod x 2 == 0 then map snd [(1,"A"),(2,"B")] else [] ) [1..10]
Does exactly what your list comprehension does. Changing the parameters would result in identical results.
Related
I just started learning Haskell and I started looking into understanding comprehension which allows me to form a subset by giving conditions to a bigger set.
I tried to make a comprehension that takes a nested list (containing other integer lists) and removes all the positive odd numbers from them and empty inner lists.
testList= [-3]:[-5,8]:[[1,3,5,7,9],[],[4,6,8,10],[1,3,6,7,9],[],[1]]
removingOnlyPosOdds xxs = [ [x | x <-xs, not (odd x && x > 0 )] | xs <- xxs, [] /= xs ]
testList before applying the comprehension function on it looked like:
[[-3],[-5,8],[1,3,5,7,9],[],[4,6,8,10],[1,3,6,7,9],[],[1]]
After applying removingOnlyPosOdds testList
The outcome was
[[-3],[-5,8],[],[4,6,8,10],[6],[]]
So what I realised that "[] /= xs" in the function description was removing the already existent
"[]" inner lists in testList only; but not the new ones that were formed caused by me removing positive odd numbers from the inner lists.
What should be my next step in order to remove those as well code wise?
I want it to look like
[[-3],[-5,8],[4,6,8,10],[6]]
Is there a way to generalise the comprehension to do it in one go?
Or is there another approach which would be much better to deal with the removal of things (like the empty inner lists )and to make a more specified set?
You can add some extra filtering, and prevent doing the same list comprehension twice with a let clause, like:
removingOnlyPosOdds xxs = [ ys | xs <- xxs, let ys = [x | x <-xs, not (odd x && x > 0 )], not (null ys) ]
Or we can just add some extra filtering, like:
removingOnlyPosOdds :: Integral i => [[i]] -> [[i]]
removingOnlyPosOdds = filter (not . null) . map (filter (\x -> not (odd x && x > 0)))
or even more pointfree:
import Control.Monad(liftM2)
removingOnlyPosOdds :: Integral i => [[i]] -> [[i]]
removingOnlyPosOdds = filter (not . null) . map (filter (not . liftM2 (&&) odd (>0)))
For example:
Prelude> removingOnlyPosOdds [[-3],[-5,8],[1,3,5,7,9],[],[4,6,8,10],[1,3,6,7,9],[],[1]]
[[-3],[-5,8],[4,6,8,10],[6]]
I have a statement [splitAt n list | n <- [1..((length list)-1)] which when runs on [1,2,3,4], gives [([1],[2,3,4]),([1,2],[3,4]),([1,2,3],[4])]
. I want to assign each pair to variables x and y
example:
x=[1]
y=[2,3,4]
x=[1,2]
y=[3,4]
etc
How to achieve this?
You can do a pattern match (in this case on the pair returned by splitAt) inside a let in a list comprehension, so you can do
[ doSomethingWith x y | n <- [1..((length list)-1), let (x, y) = splitAt n list ]
So I m watching a very basic Tutorial, and I m at list comprehension where this comes up:
listx2 = [x * 2 | x<- numberList]
with numberList being a list of numbers
So this takes every number in the list and duplicates it, so numberList = [1,2] results in [2,4].
But HOW does the whole Syntax come together?
I know that x * 2 is the doubleing, but the rest just doesn't make sense to me.
| is the "or" Symbol as far as I know,and what does it do there?
x <- numberList gives x a number from the list, but why does it take just a number? and why so nicely one after the other? There is no recursion or anything that tells it to do one element at a time...
I learn stuff by understanding it, so is that even possible here or do I just have to accept this as "thats how it goes" and memorize the pattern?
List comprehensions use their own special syntax, which is
[ e | q1, q2, ..., qn ]
The | is not an "or", it's part of the syntax, just as [ and ].
Each qi can be of the following forms.
x <- list chooses x from the list
condition is a boolean expression, which discards the xs chosen before if the condition is false
let y = expression defines variable y accordingly
Finally, e is an expression which can involve all the variables defined in the qi, and which forms the elements in the resulting list.
What you see is syntactical sugar. So Haskell does not interpret the pipe (|) as a guard, etc. It sees the list comprehension as a whole.
This however does not mean that the <- are picked at random. Actually list comprehension maps nicely on the list monad. What you see is syntactical sugar for:
listx2 = do
x <- numberList
return x*2
Now a list type [] is actually a monad. It means that we have written:
listx2 = numberList >>= \x -> return (x*2)
Or even shorter:
listx2 = numberList >>= return . (*2)
Now the list monad is defined as:
instance Monad [] where
return x = [x]
xs >>= k = concat $ fmap k xs
So this means that it is equivalent to:
listx2 = numberList >>= return . (*2)
listx2 = concat (fmap (return . (*2)) numberList)
listx2 = concat (fmap (\x -> [2*x]) numberList)
Now for a list fmap is equal to map, so:
listx2 = concat $ map (\x -> [2*x]) numberList
listx2 = concatMap (\x -> [2*x]) numberList
so that means that for every element x in the numberList we will generate a singleton list [2*x] and concatenate all these singleton lists into the result.
Hi I am trying to implement a simple function that does this with list comprehension:
duplicate "asdf" = "assdff"
duplicate "123456" = "122344566"
and this is what I came up with
duplicate xs =
[ y | x <- xs
, i <- [0..]
, y <- if i `mod` 2 == 0
then replicate 2 x
else x ]
I wanted i to act as a counter to keep track of the position of the list, and x to hold the list.
Is there a way to make this work?
The reason yours is not working is that your else branch does not produce an list inside your function - you can easily fix the syntax issue like this:
duplicate xs =
[ y | x <- xs
, i <- [0..]
, y <- if i `mod` 2 == 0
then replicate 2 x
else [x] ]
but this would give you nothing but an endless list of the first thing in xs (because there are infinite many is)
Now my guess is that you indeed want to replicate every other element 2 times and yes zip is a great idea and you are almost there (with your comment):
just make sure that you fix the syntax/type error there as well:
duplicate xs =
[ y | (i, x) <- zip [0..] xs
, y <- if i `mod` 2 == 0
then replicate 2 x
else [x] ]
this will give you:
λ> duplicate "Hello"
"HHellloo"
which is hopefully what you are looking for
exercise
You can rewrite this into
duplicate = concat . zipWith replicate (cycle [2, 1])
try to find out how this works
Hint: it's based on the idea of: take 2 of the first, then 1 of the second, then 2 of the third, then 1 of the forth, ... - only obfuscated by Haskells higher-order function and operator zoo ;)
You can use zip to provide items with indexes:
Prelude> zip "asdf" [0..]
[('a',0),('s',1),('d',2),('f',3)]
Given a list of list:
xss = [[1,2,3],[4,5,6],[7,8]]
I want to filter it by list size then by remainder and then return a list of Int.
Here are my two attempts:
concat [[x | x <- xs, mod x 2 == 0] | xs <- xss, length xs > 2]
filter (\x -> mod x 2 == 0) $ concat $ filter (\x -> length x > 2) xss
Is there more expressive way to do the same but with less code?
There's a built-in even function in Haskell, and you can also get help converting to point-free style using Blunt. This gives you:
filter even . concat . filter ((> 2) . length)
A single list comprehension suffices
[x | xs#(_:_:_:_) <- xss, x <- xs, mod x 2 == 0]
The pattern (_:_:_:_) matches all lists having at least three elements.