I was wondering, how can we do multiple things in a Haskell function?
For example, I have this code:
number = 0
increment :: Int -> Int
increment i = i + 1
function :: String -> String
function s = s ++ " world" AND increment number
I was wondering, how can we do something like this? I'm searching everywhere but I don't find a solution for this simple example :O
number
0
:function "hello"
"hello world" (AND the number = 1 now)
number
1
note: I know that AND is not a syntax in Haskell but I wanted to make you understand what I wanted to say :)
You can't modify variables in Haskell (they are not, in fact, variable). What you can do is return new values:
f (string, number) = (string ++ " world", number + 1)
which would be used like this:
Prelude> f ("hello", 0)
("hello world",1)
If you called this function with a variable instead of a literal, it would not change the value of that variable, because Haskell is designed to prevent that from happening:
Prelude> let n = 0
Prelude> f ("hello", n)
("hello world",1)
Prelude> print n
0
the problem is: in haskell you don't have "mutable states" like in other programming languages: so "number = 1" wouldn't work in terms of "setting a State"
you could combine them as a result:
increment :: Int -> Int
increment i = i + 1
stringCombine :: String -> String
stringcombine str = str ++ "world"
combine i str = (increment i, stringCombine str)
or if you have a function with a side effect (like print, putStrLn) you have to use the IO Monad:
doSideEffects :: Int -> String -> IO Int
doSideEffects i str = do
putStrLn $ str ++ "world"
return $ increment i
Related
I'm trying to learn how to use Haskell and now I have to make a program that takes a integer n and a string k and every letter of that string will be moved n places to the right in the alphabet. At this moment I've got the next code:
import Data.Char
main = do
x <- read getLine :: Int
y <- getLine
caesar x y
result :: String
rotate :: Int -> Char -> [Char]
rotate a b = [chr ((a + ord b) `mod` ord 'z' + ord 'a')]
caesar :: Int -> String -> ()
caesar moving text= do
rotatespecific moving text 0
putStrLn result
rotatespecific :: Int -> String -> Int -> ()
rotatespecific moving text place = do
if place < length text
then
result ++ rotate (moving (text !! place))
rotatespecific (moving text (place + 1))
else
if place == length text
then
result ++ rotate (moving (text !! place))
But I can't compile it because it still gives me the same error message:
parse error (possibly incorrect indentation or mismatched brackets)
|
28 | result ++ rotate (moving (text !! place))
| ^
But I can't see what's wrong with my syntax. I first thought it had something to do with using a Char as parameter for my function but I was wrong because text !! place should give a char and not a [char]. So what's wrong with what I'm doing?
After some edit I got this, but it still doesn't work:
import Data.Char
main = do
xr <- getLine
let x = read xr :: Int
y <- getLine
putStrLn (rotatespecific (x y 0))
rotate :: Int -> Char -> [Char]
rotate a b = [chr ((a + ord b) `mod` ord 'z' + ord 'a')]
rotatespecific :: Int -> String -> Int -> String
rotatespecific moving text place = do
if place < length text
then do
help <- text !! place
h <- rotate (moving help)
a <- rotatespecific (moving text (place + 1))
b <- h ++ a
return b
else
if place == length text
then do
return rotate (moving (text !! place))
else
return ()
The immediate problem is that every if must have an else. You got a parse error at the end because the parser is expecting more, namely an else for that if place == length text.
When you fix this you will have more problems, because you are treating Haskell like an imperative language, and that's not how she likes to be treated. It seems like you think
result ++ newstuff
will mutate result, adding newstuff to the end of it. But Haskell doesn't mutate. Instead, this expression result ++ newstuff is the list that results when you concatenate result and newstuff, but result itself remains unchanged.
ghci> let result = [1,2,3]
ghci> result ++ [4,5,6]
[1,2,3,4,5,6]
ghci> result
[1,2,3]
rotatespecific must return the rotated string, rather than trying to mutate it into existence. The only way functions may communicate is by returning results computed from their arguments -– they may not manipulate any "global" state like result. A function that returns () is guaranteed to be useless.
rotatespecific :: Int -> String -> Int -> String
Delete the result "global variable" (which does not mean what you think it means) and focus on defining rotatespecific in a way that it returns the rotated string.
I would also recommend commenting out main and caesar for now until you have rotatespecific compiling and working when you test it in ghci.
I feel like this is an appropriate time to just show an example, because there are a lot of little problems. I'm not going to fix logic bugs, but I've fixed your syntax. Hopefully this gets you unstuck.
rotatespecific :: Int -> String -> Int -> String
rotatespecific moving text place =
if place < length text then
-- use let .. in instead of do/bind (<-) in pure functions.
let help = text !! place
-- multiple arguments are given after the function, no parentheses
h = rotate moving help
-- use parentheses around an argument if it is a complex expression
-- (anything more than a variable name)
a = rotatespecific moving text (place+1)
b = h ++ a
in b
else
if place == length text then
rotate moving (text !! place)
else
undefined -- you must decide what String to return in this case.
After you have this function working as intended, and only then, open this sealed envelope. ♥️
rotatespecific :: Int -> String -> String
rotatespecific moving text = concatMap (rotate moving) text
I'm attempting to write a function that will continually loop checking if a randomly generated int is less than 5, if it is less than 5 then "e" is appended to a string, once "eee" is generated then exit out of the loop.
This Haskell code prints if a random value between 1 - 10 is less than 5 :
useInt :: Int -> Int
useInt x = x
test :: IO ()
test = do
let l = "eee";
int <- randomRIO (1, 10) :: IO Int
if(int < 5) then
putStrLn "less"
else
putStrLn "greater"
test
But I'm unsure how to modify a string without introducing mutable state.
To achieve this using pseudo haskell code can use :
var mutableString = "" :
useInt :: Int -> Int
useInt x = x
test :: IO ()
test = do
let l = "eee";
int <- randomRIO (1, 10) :: IO Int
while(mutableString != "eee"){
if(mutableString == "eee")
break out of loop
else
if(int < 5) then
mutableString = mutableString + "e"
putStrLn "less"
else
putStrLn "greater"
}
test
Any pointers to translate above pseudo code to valid Haskell ?
Use recursion:
test :: IO ()
test = let
loop "eee" = putStrLn "ending" -- end of the "loop"
loop l = do -- "loop" iteration
int <- randomRIO (1, 10) :: IO Int
if int < 5
then do
putStrLn "less"
loop l -- same value for l
else do
putStrLn "greater"
loop ('e':l) -- updated value for l
in loop "" -- "loop" start with initial value for l
The idea is that loop l takes as a parameter the current value of the "mutable" l. When we recurse, we pass the new value of l. In the then branch above we pass the same value since we don't want to modify it. In the else branch we prepend an 'e' character.
I am trying to write a function in haskell that would take an integer and return a concatenated (number of times the input) string
For Instance,
Input: 3
Output: hi1\nhi2\nhi3
main = do
let str = func 2 ""
putStrLn str
func :: Int -> String -> String
func i str = do
if i>(-1)
then do
str ++ "hi" ++ (show i)
func (i-1) str
else str
Thanking you!
This is a much more idiomatic solution than using if-else
a function that would take an integer and return a concatenated (number of times the input) string
func :: Int -> String -> String
func 0 s = ""
func n s = s ++ func (n - 1) s
main = putStrLn (func 3 "hi")
Output
hihihi
I wonder if 'logarithmic' solution is faster:
main = putStrLn $mul 7 "Hi"
mul :: Int -> String -> String
mul 0 _ = ""
mul 1 s = s
mul _ "" = ""
mul n s = let
(q, r) = n `quotRem` 2
s' = mul q s
in (if r == 1 then s else "") ++ s' ++ s'
The easiest way to make your code "work" (I'll explain the double quotes later) is to call func with the concatenated string as a parameter directly, without intermediate steps:
func :: Int -> String -> String
func i str = do
if i > (-1)
then func (i-1) (str ++ "hi" ++ (show i) ++ "\n")
else str
I also added the newline character to the output, which means that the last character of the result will be a new line. Therefore it is better to write
let str = func 2 ""
putStr str
That way you'll avoid an extra new line at the end.
I wrote "works" in double quotes in the first sentence, because my code prints
hi2
hi1
hi0
You need to modify func so that the lines are printed in reverse order. Hint: you can store the lines in a list and reverse the list at the end.
P.S. I'm not sure whether zero should be a valid suffix. If not, then you have to change the condition in your if statement.
I'm trying to create a function for a silly IRC bot that will return a phrase where some of the letters are repeated a random number of times. The problem I'm having is that I can't find a way to use random numbers that ghc likes. It seems that even using this answer isn't being particularly helpful for getting my code to compile.
import System.Random
-- Write bad
baaad x y = "B" ++ (repeatA x) ++ "D " ++ (exclaim y)
-- StartHere
randomBad :: String
randomBad = do
x <- randomRIO(5,10) :: IO Int
y <- randomRIO(0,6) :: IO Int
return $ baaad x y
repeatA :: Int -> String
repeatA x = rptChr "A" x
exclaim :: Int -> String
exclaim x = rptChr "!" x
rptChr :: String -> Int -> String
rptChr x y = take y (cycle x)
Even with the trick of using a do block and passing the IO Ints to the function that way, I'm still getting compile errors that it found an IO Int when expecting Int.
randomBad is not in the IO monad.... It is type String, but you are defining it to be type IO String
Change this
randomBad :: String
to this
randomBad :: IO String
Then you should be able to use this in another IO action, like main:
main = do
theString <- randomBad
putStrLn theString
I am currently working through SICP with Haskell. Exercise 1.15 asks how many times a function is called. The idea is probably that you should use the substitution method, but I would like to know how to do so in code.
In an imperative language one can keep a global variable and increment it every time the function is called. But how would you go about it in Haskell (or the pure functional way)?
You can use the Writer monad to accomplish this, provided that all of the calls to the function in question can be grouped together into a do block:
import Control.Monad.Writer
myFunc :: Int -> Int -> Writer (Sum Int) Int
myFunc a b = tell (Sum 1) >> return (a + b)
callMyFunc :: ((Int, Int, Int), Sum Int)
callMyFunc = runWriter $ do a <- myFunc 2 3
b <- myFunc 8 7
c <- myFunc 3 5
return (a, b, c)
main = putStrLn $
"myFunc was called "
++ show (getSum $ snd callMyFunc)
++ " times and produced "
++ show (fst callMyFunc)
Which outputs:
myFunc was called 3 times and produced (5,15,8)
It sounds to me like you need to have some kind of counter regardless of whether you go with a functional or a non-functional way. In Haskell, you could use the State Monad to keep track of the state:
import Control.Monad.State
someFunc x = do
num <- get
put (num + 1)
return $ x * x
runSomeFuncs = do
someFunc 1
someFunc 2
someFunc 3
main = do
let (res, state) = runState runSomeFuncs 0
putStrLn ("result: " ++ (show res))
putStrLn ("# of calls: " ++ show state)
Here, you want to keep track of how many times someFunc got called, so we pass an integer in as the state and increment the integer every time the function gets called by using:
num <- get
put (num + 1)
and then increment it by 1 and put it back. If you run this script, it should print
result: 9
# of calls: 3