Haskell - Decode message with pattern - string

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

Related

Getting [Char] instead of IO() String

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.

Haskell - Pattern Matching form (x:y:zs)

I am having difficulty understanding how to use pattern matching in guards.
I have this sample function, whose purpose is to return the last character in a string.
myFun :: [Char] -> Char
myFun str#(f:s:rst)
| str == "" = error "0 length string"
| length str == 1 = head str
| rst == "" = s
| otherwise = lame (s:rst)
It is failing with "Non-exhaustive patterns in function" when passed a string with a single character.
I assume that Haskell realizes it can't use the form (f:s:rst) to match a single element list, and then fails prior to trying to evaluate the call to length.
How do I make a guard that will tell Haskell what to do when there is only a single element?
You are pattern matching at the function definition level. The way you have described it, you are only covering the case where the string is at least two characters long:
myFun str#(f:s:rst)
You need to handle other cases as well. You can have a catch-all handler like this (needs to go as the last pattern):
myFun _ = ...
Or if you want to handle, for instance, the empty string, like this (prior to the catch-all):
myFun [] = ...
As to the purpose of your function, you are probably better off just using pattern matching and not using guards.
myFun :: [Char] -> Char
myFun [] = error "empty string"
myFun [x] = x
myFun (x:xs) = myFun xs
(Note that it would be more idiomatic to return a Maybe Char instead of crashing your program)
Based on the particularly helpful answer from Chad Gilbert, and some additional tinkering,
I have found a way to have my cake and eat it to.
In case anyone has a similar stumbling block, here is a way to specify uncovered cases prior to declaring your guards:
myFun :: [Char] -> Char
myFun "" = ""
myFun str#(s:rst)
| rst == "" = s
| otherwise = myFun (s:rst)
This also works with multiple args :
strSplit :: [Char] -> [[Char]] -> [[Char]]
strSplit str [] = strSplit str [""]
strSplit "" _ = [""]
strSplit str#(s1:ns) list#(x:xs)
| s1 == '|' = strSplit ns ("":list)
| ns == "" = map reverse $ ((s1 : x) : xs)
| otherwise = strSplit ns ((s1 : x) : xs)
Or with stuff using the original pattern#(first:second:rest) idea:
lastTwo :: [Char]->[Char]
lastTwo "" = ""
lastTwo [x] = [x]
lastTwo str#(f:s:rst)
| rst =="" = [f,s]
| otherwise = lastTwo (s:rst)
This is probably super obvious to folks more familiar with Haskell, but I didn't realize that you were "allowed" to just declare the function multiple times using different syntax to cover different cases.

How to search for a Char in a given location in a list of Strings, and then separate them into sublists?

I'm sorry if it sounds really confusing, but I just can't explain it any better than that. I'm trying to take a Char from a user and filter words from a word bank that have the given Char in a specific place. For example, if the Char given was 'e':
---- matches ["ally","cool","good"]
-e-- matches ["beta","deal"]
e--e matches ["else"]
--e- matches ["flew","ibex"]
---e matches ["hope"]
I'll need to then take the largest list and return it as the new word bank, then repeat until there is only one word left. It's a difficult thing for me to wrap my head around. Any tips?
While many people's first choice would be regular expressions, this task can be done pretty easily without them. First, you need to decide on a data type. I would make one that represents my pattern in an easier to use form than just a string:
data Character = Wildcard | Literal Char deriving (Eq, Show)
type Pattern = [Character]
buildPattern :: String -> Pattern
buildPattern [] = []
buildPattern ('-':rest) = Wildcard : buildPattern rest
buildPattern (x:rest) = Literal x : buildPattern rest
-- buildPattern "-e---" = [Wildcard, Literal 'e', Wildcard, Wildcard, Wildcard]
Now we need to build a way to match a string against a pattern, this is the real meat of the problem, and I will intentionally leave some holes for you to fill in
match :: Pattern -> String -> Bool
match [] "" = True -- An empty pattern matches an empty string
match [] _ = False -- An empty pattern doesn't match a non-empty string
match (Wildcard:pat) (_:rest) = ???
match (Literal c:pat) (x:rest)
| c == x = ???
| otherwise = False
I'll leave it up to you to figure out what to put in those holes. Remember that the Wildcard should match any character, and a Literal should match only that exact character. You'll have to use recursion here, but it isn't too difficult. If you get stuck, comment and tell me how far you got.
Now, you could also solve this without making a new data type at all and just using built-in Haskell functions. Something like
type Pattern = String
match :: Pattern -> String -> Bool
match pat str = and [c == x | (c, x) <- zip pat str, c /= '-']
(This isn't quite right on purpose, it doesn't check for the lengths to be the same.)
However, I would recommend against this. Why? What if you suddenly got the requirement that your patterns need to also handle the form --[ea]- to match both then and than? With the data type representation, you can easily extend it to be
import Data.List (span)
data Character = Wildcard | Literal Char | Class [Char] deriving (Eq, Show)
type Pattern = [Character]
buildPattern :: String -> Pattern
buildPattern [] = []
buildPattern ('-':pat) = Wildcard : buildPattern pat
buildPattern ('[':pat) = Class chars : buildPattern (drop 1 rest)
where (chars, rest) = span (/= ']')
-- span (/= ']') "ea]-" == ("ea", "]-")
-- essentially splits at a condition, but we want to drop the ']'
-- off of it so I used (drop 1 rest)
match :: Pattern -> String -> Bool
match [] "" = True
match [] _ = False
match (Wildcard :pat) (_:rest) = ...
match (Literal c:pat) (x:rest) = ...
match (Class cs:pat) (x:rest) = ...
And you can just continue build up your pattern language very easily to support many different kinds of patterns.
If you want to use a Map to store your word bank, you could do something like
-- This is recommended because many functions in Data.Map conflict with built-ins
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
-- code from above goes here
wordBank :: M.Map Int [String]
wordBank = M.fromList
[ (1, ["a"])
, (2, ["as", "at", "or", "on", "in", "is"])
, (3, ["and", "the", "and", "are", "dog", "cat"])
, (4, ["then", "than", "that", "bath", "from", "moon")
-- ...
]
wordsOfLength :: Int -> [String]
wordsOfLength len = fromMaybe [] $ M.lookup len wordBank
-- Default to an empty list if the lookup fails (i.e. len = -1)
wordsMatching :: Pattern -> [String]
wordsMatching pat =
let possibles = wordsOfLength $ length pat
in filter (match pat) possibles
-- Or just
-- wordMatching pat = filter (match pat) $ wordsOfLength $ length pat
Another way of doing this is by using the Data.List.Split library. There is a very cool function called splitOn. So if you have a pattern string
[ghci] let pat = "e--e"
[ghci] import Data.List.Split
[ghci] splitOn "e" pat
Loading package split-0.2.2 ... linking ... done.
["","--",""]
[ghci]
Notice that you only need the lengths of the values within the arrays. Hence, if you create a function that gets the length of each of the items within ...
[ghci] let f c str = map length $ splitOn [c] str
Then, this function can be used for:
[ghci] f 'e' pat
[0,2,0]
[ghci] f 'e' "else"
[0,2,0]
[ghci] f 'e' "beta"
[1,2]
Now lists can directly be compared as below ...
[ghci] [0,2,0] == [0,2,0]
True
[ghci] [0,2,0] == [0,2,1]
False
[ghci]
So your match function should take a pattern and character and another string and return of the lengths of the splits match or not.
[ghci] let g c pat str = (f c pat) == (f c str)
[ghci] g 'e' pat "else"
True
[ghci] g 'e' pat "whatever"
False
So finally, you can partically apply this function and map to a list of strings ...
[ghci] map (g 'e' pat) $ words "cafeen else I die!!"
[False,True,False,False]
Or match any other pattern ...
[ghci] map (g 'e' "-e--") $ words "This beta version wont deal with everything"
[False,True,False,False,True,False,False]
And also ...
[ghci] map (g 'e' "----") $ words "This beta version wont deal with everything"
[True,False,False,True,False,True,False]
EDIT:
Dictionaries in Haskell are part of the Data.Map module. Dictionaries comprise of name-value pairs. Now functions in Haskell are first-class values. Hence, the named arguments in the dictionaries can be functions. So you set up a list of named conditions, with functions as their values ...
You can create a dictionary of conditions like so:
[ghci] import Data.Map
[ghci] let someDict = fromList [("C1", Data.List.map (g 'e' "----") . words),
("C2", Data.List.map (g 'e' "-e--") . words)]
Then you can lookup a function and just call it. (Note that since this function will be within a Maybe, youll need to apply it like a Functor ...
[ghci] import Control.Applicative
[ghci] Data.Map.lookup "C2" someDict <*> Just "This beta version wont deal with everything"
Just [False,True,False,False,True,False,False]
Hope this helps ...

Haskell - string function

I need to write function, which is seeking for "Z" in string, and when this function finds it on i index, it appends i+3 Char to table.
Here is my code:
someFun :: String => String -> String -> String
someFun "" (r:rs) = (r:rs)
someFun (a:b:c:d:xs) (r:rs)
| a == "Z" = someFun xs ((r:rs)++d)
| otherwise = someFun (b:c:d:xs) (r:rs)
I got bunch of errors that I don't know how to fix due to my poor experience in Haskell programming.
EDIT:
If input is "(C (N (Z 'p')) (A (K (Z 'p') (Z 'q')) (Z 'r')))"
its output should be: ['p','q','r']
The specification is not entirely clear, but it sounds like you want to collect all the characters which occur three places after a 'Z' in the input, so that from
"BUZZARD BAZOOKA ZOOM"
we get
"RDKM"
Without a clearer presentation of the problem, it is difficult to give precise advice. But I hope I can help you get past some of the small irritations, so that you can engage with the actual logic of the problem.
Let's start with the type. You have
someFun :: String => String -> String -> String
but left of => is the place for properties of type expressions, usually involving variables that could stand for lots of types, such as Eq a (meaning that whatever type a is, we can test equality). String is a type, not a property, so it cannot stand left of =>. Drop it. That gives
someFun :: String -- input
-> String -- accumulating the output (?)
-> String -- output
It is not clear whether you really need an accumulator. Suppose you know the output for
"ZARD BAZOOKA BOOM" -- "DKM", right?
Can you compute the output for
"ZZARD BAZOOKA BOOM" -- "RDKM"
? Just an extra 'R' on the front, right? You're using tail recursion to do the next thing, when it is usually simpler to think about what things should be. If you know what the output is for the tail of the list, then say what the output is for the whole of the list. Why not just map input to output directly, so
someFun :: String -> String
Now, pattern matching, start with the simplest possible pattern
someFun s = undefined
Can you see enough about the input to determine the output? Clearly not. It matters whether the input is empty or has a first character. Split into two cases.
someFun "" = undefined
someFun (c : s) = undefined -- c is the first Char, s is the rest of the String
It also matters whether the first character is 'Z' or not. Be careful to use single quotes for Char and double quotes for String: they are different types.
someFun "" = undefined
someFun ('Z' : s) = undefined -- the first Char is Z
someFun (c : s) = undefined
In the case wit 'Z', you also want to make sure that s has at least three characters, and we care about the third, so
someFun "" = undefined -- input empty
someFun ('Z' : s#(_ : _ : d : _)) = undefined -- first is 'Z' and d is 3 later
someFun (c : s) = undefined -- input nonempty
The # is an "as pattern", allowing me to name the whole tail s and also check that it matches (_ : _ : d : _), grabbing the third character after the 'Z'.
So far, I've given no thought to the output, just what I need to see about the input. Let's figure out what the output must be. In the first case, empty input gives empty output
someFun "" = ""
someFun ('Z' : s#(_ : _ : d : _)) = undefined -- first is 'Z' and d is 3 later
someFun (c : s) = undefined -- input nonempty
and in the other two cases, we can assume that someFun s already tells us the output for the tail of the list, so we just need to figure out how to finish the output for the whole list. In the last line, the output for the tail is just what we want.
someFun "" = ""
someFun ('Z' : s#(_ : _ : d : _)) = undefined -- first is 'Z' and d is 3 later
someFun (c : s) = someFun s
But in the case where we've found that d is three places after the initial 'Z', we need to make sure d is at the start of the output.
someFun "" = ""
someFun ('Z' : s#(_ : _ : d : _)) = d : someFun s
someFun (c : s) = someFun s
Just checking:
*Main> someFun "BUZZARD BAZOOKA ZOOM"
"RDKM"
The key idea is to figure out how to express the output for the whole input in terms of the output for its pieces: what it is, not what to do. Here, you can assume that the output for the tail, s is correctly computed, so you just need to figure out whether you have anything extra to return.
It's not really clear what you're trying to do but this compiles:
someFun :: String -> String -> String
someFun "" (r:rs) = (r:rs)
someFun (a:b:c:d:xs) (r:rs)
| a == 'Z' = someFun xs ((r:rs)++[d])
| otherwise = someFun (b:c:d:xs) (r:rs)
The String => is for typeclass constraints, which you don't need.
d is a Char while (++) is defined on lists (of Chars in this case).
Your function has incomplete pattern matches, so you could also define those, which will simplify the existing cases:
someFun :: String -> String -> String
someFun _ [] = error "Empty string"
someFun "" s = s
someFun ('Z':b:c:d:xs) s = someFun xs (s++[d])
someFun (_:b:c:d:xs) s = someFun (b:c:d:xs) s
someFun _ _ = error "String was not in the expected format"
To display it on the screen you can use putStrLn or print:
displaySomeFun :: String -> String -> IO ()
displaySomeFun s1 s2 = putStrLn (someFun s1 s2)
Lee showed how you get it to compile.
There are still some things to say about:
You have to provide more pattern-cases, You get an error for example if you try to run someFun "" "", or someFun "A" "ABCD"
First improvement: Change (r:rs) to rs, you never use r, so you can change it to a more general case (that will fix the error on someFun "" "").
The other thing is, that you don't pattern match on lists with one, two, or tree elements.
You could add someFun _ rs = rs, so that in those cases nothing happens.
Read about head and tail.It is easier with them.And end the cycle when the length of your first list is less than 4.
someFun [] rs = rs
someFun xs rs
| (length xs) < 4 = rs
| (head xs) == 'Z' = someFun (tail xs) (rs ++ [head (tail (tail (tail xs)))])
| otherwise = someFun (tail xs) rs
You can take advantage of how failing pattern-matches work in list comprehensions and the Data.List.tails function:
import Data.List (tails)
someFun :: String -> String
someFun s = [x | 'Z':_:_:x:_ <- tails s]
The tails function gives you all tails of a list (remember that a String ist just a list of Char), e.g.:
λ: tails "Overflow"
["Overflow","verflow","erflow","rflow","flow","low","ow","w",""]
The pattern ('Z':_:_:x:_) matches any string which starts with a Z and is at least four characters in size. For each pattern match, the character which is three positions after Z is extracted.
The magical part about it is that when the pattern fails (e.g. for tails which don't start with a Z or which are too short), the element is skipped silently and doesn't contribute to the result - exactly what you seem to request.

Irrefutable pattern failed inside of Let statement

I'm learning haskell and am currently trying to parse Integers and Floats from strings.
However, when trying my readNum function on "342" or any "number" that doesn't have a single or more non-numeric characters ghci reports to me:
* Exception: parse.hs:125:18-46: Irrefutable pattern failed for pattern (i, (a
: as))
data Token
= IntTok Int | FloatTok Float | EOF
readNum :: String->(Token, String)
readNum [] = (EOF, [])
readNum xs = let (i, (a:as)) = span isDigit xs --This is line 125
in (case a of
('.') -> let (j, (b:c:bs)) = span isDigit as
in (if ((toLower b) == 'e' && (c == '+' || c == '-' || (isDigit c)))
then (let (k, d) = span isDigit bs in (FloatTok (read (concat [i,[a],j, [b],[c],k])::Float), d))
else (FloatTok (read (concat [i,[a],j])::Float), (b:c:bs)))
_ -> (IntTok (read i::Int), (a:as)))
Is there a better way to handle the case when span isDigit xs returns an empty list as the second element of the tuple?
-Thanks
You're getting the error because if you use a simple Integer like "342" then span isDigit "342" is just ("342",[]), which can't match (l,a:as). A pattern that is supposed to always match is called an irrefutable pattern. As you've found out, patterns in let bindings are irrefutable, so...
You need to to stick to patterns that will always match in a let binding. For example you could do
readNum xs = let (i, ps) = span isDigit xs
in (case ps of
('.':as) -> let (j, qs) = span isDigit as in case qs of
b:c:bs -> if ..........
_ -> error "not enough stuff after decimal number"
_ -> ....
I gave a silly error message, but clearly you should write more sensible code there.

Resources