haskell - let/where equivalent within list comprehension? - haskell

Is there a way to use let,where or otherwise define sub-expressions in a list comprehension so that it can be used both in the term and constraint?
From my experimenting, the following work:
[let x = i*i in x | i<-[1..10], i*i > 20] --good
[i*i | i<-[1..10], let x=i*i in x > 20] --good
But these do not bc of scope:
[let x = i*i in x | i<-[1..10], x > 20] -- 'x' not in scope error
let x = i*i in [x | i<-[1..10], x > 20] -- 'i' not in scope error
[x | i<-[1..10], x > 20] where x = i*i --parse error on 'where'
So let works in one place or the other, but not both together!
The only way I've found to make it work (that is, avoid repeated expressions and possibly evaluations) is to add a silly singleton-list as I did here with x<-[cat i [1..k] as a constraint to the list comprehension:
> let cat x l = foldl1 (++) $ map show [x*i | i<-l]
maximum [x| i<-[1..9999], k<-[2..div 10 $ length $ show i], x<-[cat i [1..k]], sort x == "123456789"]
"932718654"
Or, contunuing the trivial example above,
[x | i<-[0..10], x<-[i*i], x > 20] --works
This seems a little silly, and is slightly lacking in clarity, tho it doesn't seem too inefficient. Still, it would be nice if let or where worked across the whole comprehension. Can this be done?

You write it like this:
[x | i <- [0..10], let x = i*i, x > 20]
Notice there is no in. You can refer to x in both the term, and any constraints following the let. This form of let corresponds to the one in do-notation:
do i <- [0..10]
let x = i*i
guard (x > 20)
return x
Here x is in scope from the let to the end of the do-block.

You almost had it; you can simply write [x | i <- [0..10], let x = i*i, x > 20] (note the , instead of in). It's very similar to do-notation (in fact, you can use do-notation instead, and a recent GHC extension enables you to use list comprehensions for arbitrary monads). If you're curious, you can find the syntax in the Haskell 98 report:
aexp -> [ exp | qual_1 , ... , qual_n ] (list comprehension, n >= 1)
qual -> pat <- exp (generator)
| let decls (local declaration)
| exp (guard)
As you can see, one of the valid qualifiers is let decls, which is exactly what you wanted.

Related

whats the advantage of using guards in Haskell?

count_instances :: (Int)->([Int])->Int
count_instances x [] = 0
count_instances x (t:ts)
| x==t = 1+(count_instances x ts)
| otherwise = count_instances x ts
i just want to know whats so good about using guards in this Question ?
A guard can be a way to write only one half of an if-then-else expression; you can omit the else and have a partial function.
-- Leave the function undefined for x /= y
foo x y | x == y = ...
You can do the same with a case statement, but it's more verbose
foo x y = case x == y of
True -> ...
It's also easier to list several unrelated conditions as a set of alternatives than it is with a nested if-then-else or case expressions.
foo x y | p1 x y = ...
foo x y | p2 x y = ...
foo x y | p3 x y = ...
foo x y = ...
vs
foo x y = if p1 x y then ...
else (if p2 x y then ...
else (if p3 x y then ... else ...))
Patterns with guards are probably the most concise way to write code that otherwise would require nested case/if expressions.
Not the least advantage is that a where clause applies to all the guards right hand sides. This is why your example could be even more concise:
count_instances :: (Int)->([Int])->Int
count_instances x [] = 0
count_instances x (t:ts)
| x==t = 1+rest
| otherwise = rest
where rest = count_instances x ts
A guard is haskell's most general conditional statement, like if/then/else in other languages.
Your code shows a straight forward implementation of counting contents of a list equal to a given parameter. This is a good example to learn how haskell's recursion works.
An alternative implementation would be
count_instances :: Int -> [Int] -> Int
count_instances i = length . filter (==i)
that reuses already existing functions from the Prelude module. This is shorter and probably more readable.

variable not in scope - list comprehesion

I'm trying construct a function that return a list with the sum of tuples elements when is higher than 100.
resultSum :: [(Integer)] -> [Integer]
resultSum (x:xs) = [ sumT | let sumT = fst x + snd x in sumT + trd x, sumT > 100 ]
trd (_,_,x) = x
I'm receiving the message:
Not in scope: `sumT'
I figured that when I use the let I'm specifying who is my variable
PS: I need to user letand list comprehesion
I assume you want to get all 3-tuples in a list of 3-tuples where the sum of the components is greater than 100 and you want somehow to use let inside the listcomprehension.
First of all: the type of your expression is not correct, it should probably be:
resultSum :: [(Integer,Integer,Integer)] -> [Integer]
Try the following solution:
resultsum xs3 = [x+y+z | (x,y,z) <- xs3, let sumT=x+y+z in sumT>100]
BTW: The let in right of the | is local to the right part of |, you cannot use it left of |. However, you can use let as a standalone clause on the right side of | (thank chi) and then you can use it on the left side of | like
resultsum xs3 = [sumT | (x,y,z) <- xs3, let sumT=x+y+z, sumT>100]

Using case for a multi-way if

Today, I found myself typing the following code:
case () of
_ | x < 15 -> ...
_ | x < 25 -> ...
_ | x < 50 -> ...
_ -> ...
The meaning of this is straight-forward enough, but it just feels... wrong to utter case (). Does anybody have a better suggestion?
I suppose since I'm branding on x, I could have written case x. But that still leaves me with nothing to actually pattern-match on; it's all about the guards. And that still feels weird.
There is nothing wrong with case (); it is the best you have for this use-case unless you want to use very recent syntactic and non-standard-extensions like GHC’s multi-way-if.
Others have mentioned that case if fine and mutli-way if exists, but I'd go for a local function via a where or let statement:
someFunction = do
x <- monadicOp
let f y | y < 5 = expr1
| y < 15 = expr2
| y < 25 = expr3
| True = expr4
f x
This is syntactically cleaner than the case statement solution and more portable than multi-way if.
EDIT:
In case it isn't clear, if the value being compared, x in this case, is already in scope when you define the guarded function (f) then you could just define a value instead:
someFunction = do
x <- monadicOp
let r | x < 15 = expr1
| x < 25 = expr2
r
You can exploit lazy evaluation to come up with something like this:
import Data.List
import Data.Maybe
import Control.Applicative
cases :: [(Bool,a)] -> a -> a
cases lst d = snd $ fromJust $ (find fst lst) <|> Just (True,d)
main = do
let x = 20
r = cases [(x < 15, putStr "15"),
(x < 25, putStr "25"),
(x < 50, putStr "50")] $ putStr "None"
in
r

Do some replacement in Haskell List Comprehensions

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

fizzbuzz in haskell?

I'm trying to write 'fizzbuzz' in haskell using list comprehensions.
Why doesn't the following work, and how should it be?
[ if x `mod` 5 == 0 then "BUZZFIZZ"
if x `mod` 3 == 0 then "BUZZ"
if x `mod` 4 == 0 then "FIZZ" | x <- [1..20],
x `mod` 3 == 0,
x `mod` 4 == 0,
x `mod` 5 == 0 ]
This isn't valid Haskell. The else branch is not optional in if ... then ... else. Rather than using if, this seems like a good opportunity to use a case statement.
case (x `rem` 3, x `rem` 5) of
(0,0) -> "fizzbuzz"
(0,_) -> "fizz"
(_,0) -> "buzz"
_ -> show x
This snippet will work for a traditional "fizzbuzz"; your code seems to be slightly different.
First of all, you're missing the else parts of your if expressions. In Haskell, if is an expression, not a statement, so the else part is mandatory.
Secondly, the list comprehension only produces any values if all the guard expressions evaluate to True. There is no number between 1 and 20 that is 0 modulo 3, 4, and 5, so you'll get no results. You'll want to use || (logical OR) to combine them instead.
Third, most definitions of FizzBuzz want you to return the number itself if it does not meet any of the other conditions. In that case, you'll want to use show to convert the number to a String.
Here's another version that can be extended to an arbitrary number of substitutions:
fizzbuzz' :: [(Integer, String)] -> Integer -> String
fizzbuzz' ss n = foldl (\str (num, subst) -> if n `mod` num == 0 then str ++ subst else str ++ "") "" ss
fizzbuzz :: [(Integer, String)] -> Integer -> String
fizzbuzz ss n = if null str then show n else str
where str = fizzbuzz' ss n
You could inline fizzbuzz' in the where clause of fizzbuzz, but I found a separate function easier for testing.
You can run it like this:
λ> mapM_ putStrLn $ map (fizzbuzz [(3, "fizz"), (5, "buzz")]) [9..15]
fizz
buzz
11
fizz
13
14
fizzbuzz
Or with extra substitutions:
λ> mapM_ putStrLn $ map (fizzbuzz [(3, "fizz"), (5, "buzz"), (7, "dazz")]) [19..24]
19
buzz
fizzdazz
22
23
fizz
In general, it helps if you give the error as well, not just the code.
But in this case, the problem is that every if needs an else clause. Keep in mind that an if statement is just an expression, so both branches must return a value of an appropriate type.
You've got several bugs in the code, by the way, but that's the only compiler error.
Here is a response to the traditional FizzBuzz problem using list comprehension + guards.
fizzbuzz = [fb x| x <- [1..100]]
where fb y
| y `mod` 15 == 0 = "FizzBuzz"
| y `mod` 3 == 0 = "Fizz"
| y `mod` 5 == 0 = "Buzz"
| otherwise = show y
Or alternatively don't be afraid to move where clause into separate succinct function for evaluating x then call that function from your list comprehension. Its better to build on small concise functions than trying to solve it in one more complex function. e.g.
fizzval x
| x `mod` 15 == 0 = "FizzBuzz"
| x `mod` 3 == 0 = "Fizz"
| x `mod` 5 == 0 = "Buzz"
| otherwise = show x
fizzbuzz = [fizzval x| x <- [1..100]]
With the help of some judicious currying, Calvin Bottoms did it in 77 characters.
http://freshbrewedcode.com/calvinbottoms/2012/02/25/fizzbuzz-in-haskell/
[max(show x)(concat[n|(f,n)<-[(3,"Fizz"),(5,"Buzz")],mod x f==0])|x<-[1..25]]
The Monad Reader contains a solution as well as many valuable insights and some more fun exercises. The solution is copied here for those who just want to see it.
fizzbuzz :: Int -> String
fizzbuzz n = (test 3 "fizz" . test 5 "buzz") id (show n)
where test d s x | n `mod` d == 0 = const (s ++ x "")
| otherwise = x
There is no x that fullfills the condition to be divisible by 3, 4 and 5 in the range 1..20.
Therefore, you would get an empty list if the example were syntactically correct, which it is not.

Resources