I want to write a simple game "guess number" - with n attempts. I want to add some conditions and hits. Is it possible to use guards inside do block ?
Here is my code:
game = return()
game n = do putStrLn "guess number: 0-99"
number<-getLine
let y = read number
let x =20
| y>x = putStrLn "your number is greater than x"
| y<x = putStrLn "your number is less than x"
| y==x putStrLn "U win!!"
| otherwise = game (n-1)
already got error
error: parse error on input ‘|’
Is it fixable with some white space, or just impossible to do?
A do expression [Haskell-report] only consists out of exp, pat <- exp, and let … statements, and the compiler will desugar these. Hence without some language extensions, you can not write guards in a do block. Furthermore it is likely not a good idea to enable that anyway. What if you for example would want to use two "guard blocks" next to each other? Then the two would "merge" and thus the guards of the first block would already eleminate (nearly) all cases.
You can use another let clause here:
game :: IO ()
game 0 = return ()
game n = do
putStrLn "guess number: 0-99"
number <- getLine
let y = read number
let x = 20
let action | y > x = putStrLn "your number is greater than x" >> game (n-1)
| y < x = putStrLn "your number is less than x" >> game (n-1)
| otherwise = putStrLn "U win!!"
action
Note that the otherwise in the original question will never get triggered, since a value is less than, greater than, or equal to another value.
Lots of problems there.
First, you can't say game = something and game n = something, so remove the game = return () line. (You may have been trying to write a type signature, but that's not one.)
Second, you can't drop into guard syntax in arbitrary places. The closest valid thing to what you wrote are multi-way if-expressions, which would let you write this:
{-# LANGUAGE MultiWayIf #-}
game n = do putStrLn "guess number: 0-99"
number<-getLine
let y = read number
let x =20
if
| y>x -> putStrLn "your number is greater than x"
| y<x -> putStrLn "your number is less than x"
| y==x-> putStrLn "U win!!"
| otherwise -> game (n-1)
Third, the Ord typeclass is supposed to be for types with a total order, so unless you're using unlawful things like NaN, you'll always have one of y>x, y<x, or y==x, so the otherwise will never be entered.
Fourth, comparing with <, ==, and > is unidiomatic and slow, since it has to keep repeating the comparison. Instead of doing that, do something like this:
case y `compare` x of
GT -> _
LT -> _
EQ -> _
You could also just use case or LambdaCase.
{-# LANGUAGE LambdaCase #-}
game :: Int -> IO ()
game n = case n of
0 -> putStrLn "used all attempts"
n ->
putStrLn "guess a number: 0 - 99" >>
(`compare` 20) . read <$> getLine >>=
\case
EQ -> putStrLn "U win"
GT -> putStrLn "your number is greater than x" >>
game (n - 1)
LT -> putStrLn "your number is less than x" >>
game (n - 1)
The other answers are very informative. Reading those led me to see that you can also call a function to solve this, e.g.
game = do putStrLn "guess number: 0-99"
number <- getLine
let y = read number
let x = 20
action y x
where
action y x
| y>x = putStrLn "your number is greater than x" >> game
| y<x = putStrLn "your number is less than x" >> game
| otherwise = putStrLn "U win!!"
Related
What my program does? Take input from user, it needs to be between 0-99.
Program ends when user guesses the number "guess" which is 45 or after taking 10 tries.
The problem is that I cant figure out the second part of this game.
guess = 45
game = do
putStrLn ("Give a number between 0 and 99")
a <- getLine
let x = read a
if x == guess then print ("You got it!") else game
You can make a helper function where you use a parameter with the number of guesses. Each time you make a recursive call where you decrement the number of guesses, if the variable is less than or equal to zero, we can stop the recursion:
guess :: Int
guess = 45
tries :: Int
tries = 10
main :: IO ()
main = game tries
game :: Int -> IO ()
game n
| n <= 0 = putStrLn "end of the game"
| otherwise = do
putStrLn "Give a number between 0 and 99"
a <- readLn
if a == guess then putStrLn "You got it!" else game (n-1)
Thank you Willem! Had to rearrange some things in the code but you gave me the idea. Thats how now it looks like:
guess = 45
tries = 10
main = zad5 tries
zad5 y = do
putStrLn ("Give a number between 0 and 99")
a <- getLine
let x = read a
if y==1 || x == guess then print ("end of the game") else zad5 (y-1)
I'm just starting to learn Haskell using the wikibook, doing fine so far since I have taken very basic courses in HS with Visual Basic and Java, but some of the exotic features of Haskell are confusing me when trying to combine more than one. One of the wikibooks has a exercise problem in writing three different strings based on certain names given as input for the user. This is fine when using if-then-else statements, but when I try to use guards I am getting a parsing error on row 6.
main = do
putStrLn "What is your name?"
n <- getLine
|(n == "Simon") || (n == "John") || (n == "Phil")
= putStrLn "Help me make this stuff"
|n == "Koen" = putStrLn "How is this a parse error"
|otherwise "Line 11 looks fine to me"
Which reads " error: parse error on input ‘|’"
Is this a problem with the guard | or the operator ||? the error lists it is on 6:9 if that helps.
EDIT: I have another question regarding a very similar topic now that someone has answered my first question. Wikibooks Haskell tutorials has listed this as another solution to their exercise, using where statements instead of if-then-else:
main = do
putStrLn "Hello, what is your name?"
name <- getLine
putStrLn (message name)
where
greatlanguage = "I think Haskell is a great programming language."
message "Simon" = greatlanguage
message "John" = greatlanguage
message "Phil" = greatlanguage
message "Koen" = "I think debugging Haskell is fun."
message _ = "Sorry, I don't know you."
Is it possible to use the || operator to somehow condense the 3 Simon, John, and Phil lines that call greatlanguage into a single line?
Guards can only be inserted in function definitions (in the form func a b | condition = ...) and case blocks (in the form case x of pattern | condition -> ...); you can't insert them inside do blocks the way you're trying to. You will need to use if ... then ... else here instead.
There's many ways to go about this. I'd suggest you consider moving that piece of code out of the do block to its own function.
for n :: String -> IO ()
foo n | n == "Simon" ||
n == "John" ||
n == "Phil" = putStrLn "Help me make this stuff"
| n == "Koen" = putStrLn "How is this a parse error"
|otherwise = putStrLn "Line 11 looks fine to me"
And then call it in your do block,
main = do
putStrLn "What is your name?"
n <- getLine
foo n
Also it may be more sensible to make your auxiliary function "pure",
for n :: String -> String
foo n | n == "Simon" ||
n == "John" ||
n == "Phil" = "Help me make this stuff"
| n == "Koen" = "How is this a parse error"
|otherwise = "Line 11 looks fine to me"
and call it via,
main = do
putStrLn "What is your name?"
n <- getLine
putStrLn (foo n)
If you prefer you can also make the 1st guard simpler,
foo n | n `elem` ["Simon", "John", "Phil"] = "Help me make this stuff"
| n == "Koen" = "How is this a parse error"
|otherwise = "Line 11 looks fine to me"
If you really want to inline it in the do block then you could adapt one of those solutions via a case construction, e.g.
main = do
putStrLn "What is your name?"
n <- getLine
putStrLn $ case n of
_ | n `elem` ["Simon", "John", "Phil"] -> "Help me make this stuff"
_ | n == "Koen" -> "How is this a parse error"
_ -> "Line 11 looks fine to me"
To complement the other answers, I'll add two alternatives (not necessarily better ones).
main = do
putStrLn "What is your name?"
n <- getLine
case () of
_ | n == "Simon" || n == "John" || n == "Phil"
-> putStrLn "Help me make this stuff"
| n == "Koen"
-> putStrLn "How is this a parse error"
| otherwise
-> putStrLn "Line 11 looks fine to me"
This requires the MultiWayIf extension to be turned on.
{-# LANGUAGE MultiWayIf #-} -- at the top of the file
main = do
putStrLn "What is your name?"
n <- getLine
if | n == "Simon" || n == "John" || n == "Phil"
-> putStrLn "Help me make this stuff"
| n == "Koen"
-> putStrLn "How is this a parse error"
| otherwise
-> putStrLn "Line 11 looks fine to me"
Remember that even after otherwise we need ->, or we trigger a parse error.
I know what an indentation error is, but I have no idea why I'm getting this error here, while every is aligned, trying to solve it for 2 hours.
Account.hs:40:25: error:
parse error (possibly incorrect indentation or mismatched brackets)
|
40 | let amount = readLn :: IO Int
| ^
Failed, 0 modules loaded.
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
I also tried this version but this also gives incorrect indentation or mismatched brackets:
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
The problem is here:
-- |<---- "column 0" of this 'do' block
then do putStrLn $ "Enter your name "
-- | still good; a 'let' statement:
let name = getLine
-- |<---- "column 0" of this 'let' block
putStrLn $ "Enter the initial amount"
-- | Huh, there's no '=' in ^this^ declaration?
let amount = readLn :: IO Int
-- ^^^ Why is there a 'let' within another let binding?
-- I still haven't seen a '='. Better throw a parse error.
Basically, putStrLn $ "Enter the initial amount" is aligned with name = ... in the preceding line, so the compiler reads it as a declaration (part of the same let block).
To fix your indentation errors, it should be:
main = do
putStrLn $ "Press one to create a new account"
let g = getLine
enteredValue = read g :: Int
if g == 1
then do putStrLn $ "Enter your name "
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
value = Account (name,1,amount) Saving
show value
else do putStrLn $ "Nothing"
But then you'll run into type errors:
read g is wrong: read takes a String, but g :: IO String
g == 1 is wrong: 1 is an Int, but g :: IO String
show value is wrong: show returns a String, but you're using it as an IO action
You haven't shown the declaration of Account, but you're likely going to have issues with name and amount, too
You probably want something like:
main = do
putStrLn $ "Press one to create a new account"
g <- getLine
let enteredValue = read g :: Int
if enteredValue == 1
then do putStrLn $ "Enter your name "
name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
putStrLn (show value)
else do putStrLn $ "Nothing"
Basically, use v <- expr to go from expr :: IO Something to v :: Something.
Other notes:
g <- getLine; let enteredValue = read g :: Int better written as enteredValue <- readLn :: IO Int
putStrLn (show value) can be shortened to print value
you don't need do for a single expression (nor $ for a single operand): ... else putStrLn "Nothing"
There is more wrong to your code than just the Indentation Errors - so my first suggestion would be reading a bit of learn you a haskell for great good.
Next there are two assignment operators in haskell - one binds the result of an action … <- … and the other one is a local definition/declaration of a pure computation let … = ….
Moreover you can improve your reading a value by taking account of the possible false input, that someone could give you (intentionally and unintentionally) by replacing read with readMaybe, where the latter returns a Maybe something, for example readMaybe "1" = Just 1 :: Maybe Int or readMaybe "foo" = Nothing :: Maybe Int.
Regarding your indentation it is best that you compare one solution to your program with yours own:
import Text.Read (readMaybe)
data Type = Saving | Checking
deriving (Show)
data Account = Account (String,Int,Int) Type
deriving (Show)
main :: IO ()
main = do
putStrLn "Press one to create a new account"
g <- getLine
let enteredValue = readMaybe g :: Maybe Int
here the result of getLine and entered value have the same scope so they have the same indentation - we only change the scope after the next if where the then-block - and the else-block do not share the 'declarations' of each branch, so you couldn't use name in the else-block, but enteredValue can be used in both.
if enteredValue == Just 1
then do putStrLn "Enter your name "
name <- getLine
putStrLn "Enter the initial amount"
amount' <- fmap readMaybe getLine
here again name and amount' share the same scope, and pattern matching on amount' creates a new scope where amount is visible and the match on Nothing where you cannot use this variable.
case amount' of
Just amount -> print $ Account (name,1,amount) Saving
Nothing -> putStrLn "Nothing"
else putStrLn "Nothing"
let is for binding values, which is done in the form let x = y+z, where x is the name (aka "identifier") being bound, and y+z is the expression to which it is being bound.
In your example, I see three bindings: name, amount, and value. The rest are not value bindings, but actions.
In the do notation, actions do not need a let. You just write them one after another. So:
let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
let value = Account (name,1,amount) Saving
show value
But wait! This is not all!
getLine is not actually an expression of type String, as you seem to be hoping here. Rather, getLine is an action. In order to get it to "run" and "produce" a String value, you need to use the <- construct instead of let:
name <- getLine
Similarly with readLn:
amount <- readLn :: IO Int
Finally, show value is not actually an action that would print the value to the screen. show is a function that takes a value and return a String. It doesn't "do" anything (i.e. doesn't produce any outside effects), so you can't use it in place of an action in the do notation. If you wanted an action that would print a value to the screen, that would be print:
print value
Gathering everything together:
name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
print value
And after fixing all of that, you'll have similar difficulties with the first part of your program, where you have let g = getLine instead of g <- getLine.
I have the following simple number guess program
import System.Random
turn :: Int -> Int -> Int -> IO ()
turn number attempt attempts =
do
if attempts == 0
then putStrLn "You lose"
else if attempt==number
then putStrLn "You got it!"
else if attempt==0
then guess number attempt attempts
else if attempt < number
then do
putStrLn "The number is greater"
guess number attempt attempts
else
do
putStrLn "The number is lesser"
guess number attempt attempts
guess :: Int -> Int -> Int -> IO ()
guess number attempt attempts =
do
putStr "Try and guess number "
g <- getLine
let number' = read g :: Int
let check = (number'==number)
let attempts' = if check then attempts else attempts - 1
turn number number' attempts'
numberGuess :: IO ()
numberGuess = do
let attempts = 5
number <- randomRIO (0, 10) :: IO Int
turn number 0 attempts
How can I clean up the ugly if else or what techniques are available in haskell?
Your outermost do in turn doesn't actually do anything. So you should nix it. Next, use pattern matching and guards to begin to clean things up.
turn _number _attempt 0 = putStrLn "You lose"
turn number attempt attempts
| attempt == number = putStrLn "You got it!"
| attempt == 0 = guess number attempt attempts
| attempt < number = do
putStrLn "The number is greater"
guess number attempt attempts
| otherwise = do
putStrLn "The number is lesser"
guess number attempt attempts
The final step to clean up turn will be factoring out the pattern "Do something, then call guess". I'll let you try that on your own. I used a pattern match for the first case to demonstrate that technique, which is very often the right one. In this particular case you might be better off just using guards.
For guess, the easy bit is just to combine some expressions.
guess :: Int -> Int -> Int -> IO ()
guess number attempt attempts = do
putStr "Try and guess number "
g <- getLine
let attempts' = if read g == number
then attempts
else attempts - 1
turn number number' attempts'
Note, however, that read generally shouldn't be used to process user input, as it will crash your program on bad input. Import Text.Read and use readMaybe, perhaps.
I am using guards inside a function but not immediately after the function signature. The guards are under a do statement inside the body of the function. I get this error:
parse error on input `|'
I thought maybe the error comes from the indentation but i have tried many indentation but still i get the errors. Am asking is it because the guards are not immedaitely after the function signature that is why am getting the errors?
thanks
UPDATE 1
CODE:
The user is suppose to guess a number , and the number will be compared with the random number if they are the same. If it is not correct then the user will guess till the "guess" variable in the function is zero. in every interation that value(guess) is decreased by one.
for instance : puzz 12 5. the user can guess for five times, the random number will be picked between 1 and 12. that is how the function is suppose to do, but it is not working.
puzz :: Int -> Int -> IO ()
puzz boundary guess = do
putStr "Guess"
-- putStr -- I have to print (1 .. guess) here in each iteration
putStr ":"
x <- randomRIO (1, boundary :: Int)
n <- getLine
let
nTo = read n::Int
in print x
| guess == 0 = putStr "You couldn't guess right,the correct answer is" ++ x
| nTo > x = putStr "lower"
| nTo < x = putStr "higer"
| nTo == x = putStr "Congrat, You guess right."
| otherwise raad boundary (guess - 1)
the ouput must be like this:
Main> puzz 50 6
Guess a number betwee 1 en 50.
Guess 1: 49
lower
Guess 2: 25
lower
Guess 3: 12
higher
Guess 4: 18
higher
Guess 5: 21
higher
Guess 6: 23
lower
You couldn't guess correct, the answer was: 22.
thanks for your help
You’re using guards incorrectly. From the report:
Top level patterns in case expressions and the set of top level patterns in function or pattern bindings may have zero or more associated guards.
So they’re only for cases and function bindings. If you just want to concisely introduce a series of true-false tests, while inside a do-notation, perhaps the case () of () trick would work:
main = do
putStrLn "hello world"
n <- getLine
let nTo = read n :: Int
case ()
of () | cond -> putStrLn "foo"
| cond' -> putStrLn "bar"
| otherwise -> putStrLn "baz"
It should be noted that there are several things that are a bit off with your code, in addition to using guards wrong. By default output is buffered in haskell so if you want Guess to be on the same line as input you have to either say that stdOut should not be buffered (hSetBuffering stdOut NoBuffering), or you have to flush output with hFlush. It's not necessary to write boundary :: Int, the compiler knows it is an Int. Here is a bit more complete example, I'm sure it could be done better but atleast it works:
import Control.Monad(liftM,unless)
import System.IO(hFlush,stdout)
import System.Random(randomRIO)
guessMyNumber upper guesses = do
putStrLn $ "Guess a number between 1 and " ++ show upper ++ "!"
randomRIO (1, upper) >>= puzz guesses
puzz 0 number = do
putStrLn $ "Sorry, no more guesses, the number was "
++ show number ++ "."
puzz guesses number = do
putStr "Guess:" >> hFlush stdout
guess <- liftM read getLine
printMessage guess number guesses
printMessage guess number guesses
| number > guess = putStrLn "higer" >> puzz (guesses-1) number
| number < guess = putStrLn "lower" >> puzz (guesses-1) number
| number == guess = putStrLn "Congratulations! You guessed right!"