The following is the haskell comprehensions, what I don't understand is the pipe symbol. What does it mean and what is its role in the function?
boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
ghci> [ [ x | x <- xs, even x ] | xs <- xxs]
[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
Its expression/syntax for list comprehensions. An easy example is the following,
import Data.Char (toUpper)
[toUpper c | c <- s]
You pass a string (list of chars), [ s ], "Hello", to the generator [ c <- s ] , this feeds each character of s to the left-hand expression [ toUpper c ], building a new list. The result of this list comprehension would be "HELLO".
Related
I have problems to understand the following piece of code:
treePositions :: Tree a -> [[Int]]
treePositions (Node _ ts) =
[] : [ (i : is ) | i <- [0..(length ts - 1)],
is <- treePositions (index ts i) ]
This function would calculate any valid paths to a position in the given tree, where for every node the edges are marked with 0..lastOutgoingEdge.
If I understood it right the index function would return the node at Index i in the node list of the tree.
index :: [a] -> Int -> a
index :: [a] -> Int -> a
index [] i = error "invalid index"
index (x:xs) 0 = x
index (x:xs) i = ith xs (i-1)
Now for given trees:
t1 = Node "a" [
Node "b" [
Node "c"[],
Node "d"[]
]
]
t2 = Node "z" []
the function would return:
treePositions t1 == [ [], [0], [0,0], [0,1] ]
treePositions t2 == [ [] ]
What I don't understand is this part:
[] : [ (i : is ) | i <- [0..(length ts - 1)],is <- treePositions (index ts i) ]
My thoughts:
If I have x|x <- [0..10], x < 2. This would translate in "take every x in [0..10] for which x < 2 applies. So if I would take every i in [0..(length ts - 1)], how would this then return lists for a condition?
In your case, the part after the comma is not a condition, but a second generator. Simplified, the notation looks like this:
> [ (a, b) | a <- [1..3], b <- [1..2] ]
> [(1,1),(1,2),(2,1),(2,2),(3,1),(3,2)]
The example compherension above means the following:
go through list [1..3], and for every element of it,
go through list [1..2], and for every element of it,
produce a tuple
Further, a generator can depend on elements of previous generators, e.g.:
> [ (a, b) | a <- [1..3], b <- [1..a] ]
> [(1,1),(2,1),(2,2),(3,1),(3,2),(3,3)]
So in your case:
[ (i : is ) | i <- [0..(length ts - 1)],is <- treePositions (index ts i) ]
the logic is this:
for every i in 0..(length ts - 1),
go through every is in treePositions (index ts i),
and produce i : is as result
Why this code:
a=array ((0,0),(5,5)) [((i,j),x) | i <- [0..5], j <- [0..5], x <- a!(i,j)]
cause error cannot construct the infinite type: e ~ [e],
but if rewrite it like this:
a=array ((0,0),(5,5)) [((i,j),a!(i,j)) | i <- [0..5], j <- [0..5]]
it works fine?
In a list comprehension, the right side of <- is a list to take elements from. But you can use let expressions:
[ ... | ..., let x = a ! (i,j) ]
In a parallel list comprehension in Haskell, I encounter a problem when trying to use a guard.
largestPalindrome :: Int -> Int
largestPalindrome x = maximum [ a*b
| a <- [x,x-1..1]
| b <- [x,x-1..1]
, isPalindrome (a*b) ]
The error that is displayed is
Variable not in scope: a :: Int
Quoted from Haskell Prime:
Parallel comprehensions extend list comprehensions with a notation for
zips. The comprehension
[ e | quals1 | ... | qualsN ]
can be desugared to
zipWithN (\ p1 ... pN -> e) [p1 | quals1] ... [pN | qualsN]
where pi
is a tuple of the variables defined by qualsi and used by e.
So, from your example, [a*b |a<- [x,x-1..1] | b <- [x,x-1..1] , isPalindrome (a*b)] is roughly equivalent to
zipWith (\a b -> a*b)
[ a | a<-[x,x-1..1] ]
[ b | b <- [x,x-1..1], isPalindrome (a*b) ]
where it is pretty clear why a is not in scope for the last list comprehension. Intuitively, you should think of each | delimited part as being completely independent from the others. Any filtering operation is going to be bound to only one of these parts.
One of the questions that came up in one of my lectures was the following:
trips :: [(Int, Int, Int)]
trips = [ (x,y,z) | z <- [2..], y <- [2..z-1], x <- [2..y-1] ]
What is the first five elements output?
Now I thought I was aware how dot dot notation worked, but when I put the above into a compiler the output is:
(2, 3, 4), (2, 3, 5), (2, 4, 5), (3, 4, 5), (2, 3, 6) etc.
How does this happen?
I thought if things begun with [2..] then all the subsequent lists would begin with two? z is defined as [2..] yet it never once displays 2 as the third int. I'm obviously missing something here but I'm not entirely sure what.
[ (x,y,z) | z <- [2..], y <- [2..z-1], x <- [2..y-1] ] =
[ (x,y,2) | y <- [2..2-1], x <- [2..y-1] ] ++
[ (x,y,3) | y <- [2..3-1], x <- [2..y-1] ] ++
[ (x,y,4) | y <- [2..4-1], x <- [2..y-1] ] ++
... =
[ (x,y,2) | y <- [2..1], x <- [2..y-1] ] ++
[ (x,y,3) | y <- [2..2], x <- [2..y-1] ] ++
[ (x,y,4) | y <- [2..3], x <- [2..y-1] ] ++
... =
[ (x,y,2) | y <- [], x <- [2..y-1] ] ++
[ (x,y,3) | y <- [2], x <- [2..y-1] ] ++
[ (x,y,4) | y <- [2,3], x <- [2..y-1] ] ++
...
Note the range y <- [2..2-1] i.e. y <- [2..1] i.e. y <- []. Because of this, there are no triples (x,y,2) to generate.
Similarly, when y is 2, the range z <- [2..y-1] will generate nothing, and we do not get triples of the form (x,2,3).
My questions is if I put in a string containing such as Hello, today is a Nice Day!! How could I get rid of spaces and punctuation and also replacing the uppercase letters with lowercase?
I know how to delete them but not how to replace them.
Also to get rid of the punctuation.
Sorry I don't know how to mess around with strings, only numbers.
testList xs = [if x = [,|.|?|!] then " " | x<-xs]
import Data.Char
If you want convert the punctuation to space and the characters from upper case to lower case:
testList xs = [if x `elem` ",.?!" then ' ' else toLower x | x<-xs]
Example: testList "TeST,LiST!" == "test list "
If you want to delete the punctuation and convert the characters from upper case to lower case:
testList2 xs = [toLower x | x<-xs, not (x `elem` ",.?!")]
Example: testList2 "Te..S,!t LiS?T" == "test list"
If you don't want or can not import Data.Char, this is an implementation of toLower:
toLower' :: Char -> Char
toLower' char
| isNotUppercase = char -- no change required
| otherwise = toEnum (codeChar + diffLowerUpperChar) -- char lowered
where
codeChar = fromEnum char -- each character has a numeric code
code_A = 65
code_Z = 90
code_a = 97
isNotUppercase = codeChar < code_A || codeChar > code_Z
diffLowerUpperChar = code_a - code_A
I've been without writing a code in Haskell for a long time, but the following should remove the invalid characters (replace them by a space) and also convert the characters from Uppercase to Lowercase:
import Data.Char
replace invalid xs = [if elem x invalid then ' ' else toLower x | x <- xs]
Another way of doing the same:
repl invalid [] = []
repl invalid (x:xs) | elem x invalid = ' ' : repl invalid xs
| otherwise = toLower x : repl invalid xs
You can call the replace (or repl) function like this:
replace ",.?!" "Hello, today is a Nice Day!!"
The above code will return:
"hello today is a nice day "
Edit: I'm using the toLower function from Data.Char in Haskell, but if you want to write it by yourself, check here on Stack Overflow. That question has been asked before.
You will find the functions you need in Data.Char:
import Data.Char
process str = [toLower c | c <- str , isAlpha c]
Though personally, I think the function compositional approach is clearer:
process = map toLower . filter isAlpha
To get rid of the punctuation you can use a filter like this one
[x | x<-[1..10], x `mod` 2 == 0]
The "if" you are using won't filter. Putting an if in the "map" part of a list comprehension will only seve to choose between two options but you can't filter them out there.
As for converting things to lowercase, its the same trick as you can already pull off in numbers:
[x*2 | x <- [1..10]]
Here's a version without importing modules, using fromEnum and toEnum to choose which characters to allow:
testList xs =
filter (\x -> elem (fromEnum x) ([97..122] ++ [32] ++ [48..57])) $ map toLower' xs
where toLower' x = if elem (fromEnum x) [65..90]
then toEnum (fromEnum x + 32)::Char
else x
OUTPUT:
*Main> testList "Hello, today is a Nice Day!!"
"hello today is a nice day"
For a module-less replace function, something like this might work:
myReplace toReplace xs = map myReplace' xs where
myReplace' x
| elem (fromEnum x) [65..90] = toEnum (fromEnum x + 32)::Char
| elem x toReplace = ' '
| otherwise = x
OUTPUT:
*Main> myReplace "!," "Hello, today is a Nice Day!! 123"
"hello today is a nice day 123"
Using Applicative Style
A textual quote from book "Learn You a Haskell for Great Good!":
Using the applicative style on lists is often a good replacement for
list comprehensions. In the second chapter, we wanted to see all the
possible products of [2,5,10] and [8,10,11], so we did this:
[ x*y | x <- [2,5,10], y <- [8,10,11]]
We're just drawing from two lists and applying a function between
every combination of elements. This can be done in the applicative
style as well:
(*) <$> [2,5,10] <*> [8,10,11]
This seems clearer to me, because it's easier to see that we're just
calling * between two non-deterministic computations. If we wanted all
possible products of those two lists that are more than 50, we'd just
do:
filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11]
-- [55,80,100,110]
Functors, Applicative Functors and Monoids