I'm a new to Haskell. I'm reading an input string (a) and want to return a string when I find a character (e) inside. Now my whole source code:
a = "b,b,b,b/b,b,b/b,b,b,b/e,e,e/w,w,w,w/w,w,w/w,w,w,w"
n = length a
simpleCase n =
case n of
'a' -> "hey cutie"
eLoopCase i =
if i < n
then do
let char = a !! i
case char of
'e' -> putStr $ "(" ++ "found an e" ++ "),"
'w' -> return ()
'b' -> return ()
',' -> eLoopCase (i+1)
'/' -> eLoopCase (i+1)
if (char == ',' || char == '/') == False
then eLoopCase (i+1)
else return ()
else return ()
simpleCase gives me back a string but eLoopCase gives me an IO() back. It works, however I'd like for eLoopCase to give an String back so I can work on it.
:t simpleCase
simpleCase :: Char -> [Char]
:t eLoopCase
eLoopCase :: Int -> IO ()
I understand that this has something to do with monads,then do and putStr, this is where my understanding ends. Removing do gives a parse error.
eLoopCase is returning nothing because you have return () at all the "ends" of it. I think you're after something like this (Notice appending the current character to the result of the recursive call in the x == 'w' || x == 'b' branch):
a :: String
a = "b,b,b,b/b,b,b/b,b,b,b/e,e,e/w,w,w,w/w,w,w/w,w,w,w"
eLoopCase :: String -> IO String
eLoopCase [] = return []
eLoopCase (x:xs)
| x == 'e' = do
putStrLn "(found an e)"
return [x]
| x == ',' || x == '/' =
eLoopCase xs
| x == 'w' || x == 'b' = do
rest <- eLoopCase xs
return (x : rest)
| otherwise = do
putStrLn ("Encountered invalid character: " ++ show x)
return []
General problems with your code:
The program you wrote is very imperative in style. This is something I would urge you to try to move away from while writing Haskell.
Prefer pattern matching on lists over indexing with !!.
I should note that the function I provided has flaws of its own. It presupposes that the strings provided to it will only consist of a couple different chars. A way to improve it would be to add an otherwise branch to the guards. (Edit: Added to the snippet above)
I think it's also worth pointing out that this function really need not depend on IO at all to work. Look into other "pure" alternatives for doing error handling/reporting such as Either, Maybe, and Writer.
I'm glad you've decided to try to learn Haskell. You can do a lot better than eLoopCase. I'll tell you how, but first I'll explain the problem you were having in your original question and how you fixed it.
As originally written, eLoopCase is has a type Int -> IO (), meaning it is a function which takes an Int and returns an input output action. IOW, the type tells you that if you give it a number it will do something. If you look at the code you can see that that something is just printing out strings, but it could have been almost anything.
In your answer, you rewrote eLoopCase to construct strings directly via the ++ operator, instead of printing them out to the terminal via putStr. This is the reason your rewritten function has the type that it does. It also explains why you no longer need the return () statements, which are the "Do nothing while returning a () value" IO actions.
I hope that clears things up a little. That said, eLoopCase can be tremendously improved.
Haskell programs are efficiently written when the control logic matches the data structure. For instance, this program is iterating through a list. A list is defined as the data which is either an empty list or an element and more list, as seen in the declaration below.
data List a = []
| a : List a
Consequently, programs which iterate through a list will be based on decisions for those two constructors. For example (using the synonym String for [Char]), eLoopCase can be rewritten as
eLoopCase' :: String -> String
eLoopCase' [] = ""
eLoopCase' (a:as) = evalCase a ++ eLoopCase' as
evalCase a = case a of
'e' -> "(" ++ "found an e" ++ "),"
'w' -> ""
'b' -> ""
',' -> ""
'/' -> ""
_ -> ""
Notice eLoopCase' needs to be fed a as an input as it is no longer hard coded into the body of the function--a good thing. It also does away with the index i and errors arising from using !! (try calling eLoopCase (-1)).
Having practice in writing recursive functions such as eLoopCase' is a good start. An evolution in programming is to see what you are intending to do with that loop and applying appropriate patterns.
For instance, since you want to evaluate every element in a list by a certain function, use a map. Specifically map evalCase to go from a list of characters to your list of strings, then use concat to append all those lists together:
eLoopCase'' = concat . map evalCase
Actually I've answered my own question. The following seems to work:
a = "b,b,b/b,b,b/b,b,b,b/e,e,e/w,w,w,w/w,w,w/w,w,w,w"
n = length a
eLoopCase i =
if i < n
then
case a !! i of
'e' -> "(" ++ "found an e" ++ ")," ++ eLoopCase (i+1)
'w' -> "" ++ eLoopCase (i+1)
'b' -> "" ++ eLoopCase (i+1)
',' -> eLoopCase (i+1)
'/' -> eLoopCase (i+1)
else ""
I'm still curious as to what happened.
Related
I have the following code:
import Debug.Trace (trace)
mtrace :: Show a => String -> a -> a
mtrace msg value =
trace (msg ++ show value) value
isVowel :: Char -> Bool
isVowel = (`elem` "AEIOU")
vowelSlice :: String -> ([Maybe Char], String)
vowelSlice "" = ([], [])
vowelSlice (c:s)
| isVowel c = (Nothing:chars, c:vowels)
| otherwise = (Just c:chars, vowels)
where (chars, vowels) = vowelSlice s
stringTogether :: [Maybe Char] -> String -> String
stringTogether [] "" = ""
stringTogehter (Just c:cs) vowels = c:stringTogether cs vowels
stringTogehter (Nothing:cs) (v:vs) = v:stringTogether cs vs
process :: String -> String
process s = stringTogether (mtrace "chars: " chars) (mtrace "vowels: " cycledVowels)
where
(chars, vowels) = vowelSlice s
cycledVowels = tail vowels ++ [head vowels]
main :: IO ()
main = do
line <- getLine
putStrLn $ process line
for testing I run my file using the runhaskell command and the I enter HELLO PEOPLE as user input once the program is running. I'm expecting the output: HELLE POEPLO or something similar since my program is meant to shift the vowels only. My program works fine until it tries to run the stringTogether method. Specifically the issue lies with the pattern matching, I have an array:
[Just 'H',Nothing,Just 'L',Just 'L',Nothing,Just ' ',Just 'P',Nothing,Nothing,Just 'P',Just 'L',Nothing]
and a pattern
(Just c:cs) vowels that I expect it to match but somehow it doesn't seem to work. When I run the code and enter HELLO WORLD I receive the following error:
18:1-25: Non-exhaustive patterns in function stringTogether I logged a few things using the trace module and everything looks as expected before entering the stringTogether function
I'm probably overlooking something really obvious but I just can't get my head around why the pattern match won't work, I hope someone is able to help. Thanks in advance!
The pattern match fails because of a typo, 2 separate functions were defined instead of the intended one: stringTogether and stringTogehter. The patterns were valid but the compiler failed to find them because they had mismatching names. The function technically stringTogether only had one pattern [] "" so when the list was passed it raised the 18:1-25: Non-exhaustive patterns in function stringTogether error.
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 am new to Haskell and I am currently learning it in school. I got a school task where I have to decode a message that contain certain patterns but I have got no idea how to do this.
The pattern looks something like this: If a letter has a consonant followed by the character 'o' and then once again followed by the same consonant as before it should replace that substring ("XoX" where X is a consonant) with only the consonant. For example if I decode the string "hohejoj" it should return "hej". Sorry if I am explaining this poorly but I think you understand.
This is the code I have so far (but it doesn't work):¨
karpsravor :: String->String
karpsravor s = karpsravor_help s ""
where karpsravor_help s res
|s == "" && (last res) == 'o' = (init res)
|s==""=res
|otherwise = karpsravor_help (drop 3 s) (res ++ (consDecode (take 3 s)))
consDecode :: String->String
consDecode a
|(length a) < 3 = ""
|a == [(head a)]++"o"++[(head a)] = [(head a)]
|otherwise = a
The code is completely broken and poorly written (dumb method) but I have no other idea for how to solve this. Please help!
Pattern match to find occurrences of 'o'. I.e., use
karpsravorhelp (a:'o':b:rest) res = ...
You can't have a:'o':a:rest in the above, you can't pattern match for equality; you'll need to use a guard to make sure that a == b:
karpsravorhelp (a:'o':b:rest) res
| a == b = ...
| otherwise = ...
You'll also have to make sure a and b are consonants, which will just be an 'and' condition for the first guard. For the otherwise condition, make sure that the recursive call calls (b:rest) since you could have something like a:'o':b:'o':b:....
Also make sure to match for two other patterns:
Empty List, []
x:rest, which must go after the above pattern; this way, it will first attempt to match on the a:'o':b:rest pattern, and if that's not there, just take the next letter.
One way to do it would be with unfoldr from Data.List. You can use a case expression to pattern match on a : 'o' : b : rest, and then check that a and b are equal and not vowels using a guard |. Then just include the base cases for when the pattern doesn't match.
notVowel :: Char -> Bool
notVowel = (`notElem` "aeiouAEIOU")
karpsravor :: String -> String
karpsravor = unfoldr $ \str -> case str of
a : 'o' : b : rest
| a == b && notVowel a -> Just (a, rest)
a : rest -> Just (a, rest)
"" -> Nothing
This is an extension of this question: Haskell replace characters in string
I would like to tweak the following expression to replace a char with a string
let replaceO = map (\c -> if c=='O' then 'X'; else c)
In the end, I would the following results (XX can be a string of any length):
replaceO "HELLO WORLD"
"HELLXX WXXRLD"
You can use concatMap:
let replace0 = concatMap (\c -> if c=='O' then "X" else "XX")
You kind formulate your problem in terms of traversing and accumulating based on a condition, something like this,
replace :: String -> Char -> String -> String
replace xs c s = foldr go [] xs
where go x acc = if x == c then acc ++ s
else acc ++ [x]
For you example:
>replace "HELLO WORLD" 'O' "XXX"
> "HELLXXX WXXXRLD"
Supposing I had the string "HELLO WORLD" is there a way I can call a function that replaces the character 'O' in the string with the character 'X' so that the new string would look like "HELLX WXRLD"?
How about:
let
repl 'o' = 'x'
repl c = c
in map repl "Hello World"
If you need to replace additional characters later, just add clauses to the repl function.
Sorry for picking up this old thread but why not use lambda expressions?
λ> let replaceO = map (\c -> if c=='O' then 'X'; else c)
λ> replaceO "HELLO WORLD"
"HELLX WXRLD"`
Alternative 1 - Using MissingH
First:
import Data.List.Utils (replace)
Then use:
replace "O" "X" "HELLO WORLD"
Alternative 2 - Using Control.Monad
One funny bastard:
import Control.Monad (mfilter)
replace a b = map $ maybe b id . mfilter (/= a) . Just
Example:
λ> replace 'O' 'X' "HELLO WORLD"
"HELLX WXRLD"
Alternative 3 - Using if
Amon's suggestions was probably the finest I believe! No imports and easy to read and understand!
But to be picky - there's no need for semicolon:
replace :: Eq a => a -> a -> [a] -> [a]
replace a b = map $ \c -> if c == a then b else c
If you depend on the text package (like 99.99% of Haskell applications), you can use T.replace:
>>> replace "ofo" "bar" "ofofo"
"barfo"
Here's another possible solution using divide and conquer:
replaceO [] = []
replaceO (x:xs) =
if x == 'O'
then 'X' : replaceO xs
else x : replaceO xs
First, you set the edge condition "replaceO [] = []".
If the list is empty, there is nothing to replace, returning an empty list.
Next, we take the string and divide it into head and tail. in this case 'H':"ELLOWORLD"
If the head is equal to 'O', it will replace it with 'X'. and apply the replaceO function to the rest of the string.
If the head is not equal to 'O', then it will put the head back where it is and apply the replaceO function to the rest of the string.
replace :: Char -> Char -> String -> String
replace _ _ [] = []
replace a b (x : xs)
| x == a = [b] ++ replace a b xs
| otherwise = [x] ++ replace a b xs
I'm new to Haskell and I've tried to make it simpler for others like me.
I guess this could be useful.
main = print $ charRemap "Hello WOrld" ['O','o'] ['X','x']
charRemap :: [Char] -> [Char] -> [Char] -> [Char]
charRemap [] _ _ = []
charRemap (w:word) mapFrom mapTo =
if snd state
then mapTo !! fst state : charRemap word mapFrom mapTo
else w : charRemap word mapFrom mapTo
where
state = hasChar w mapFrom 0
hasChar :: Char -> [Char] -> Int -> (Int,Bool)
hasChar _ [] _ = (0,False)
hasChar c (x:xs) i | c == x = (i,True)
| otherwise = hasChar c xs (i+1)