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

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.

Related

Haskell: Replace a subString in a String without Data.List package

I'm very new in Haskell and I want to know how I can replace a predetermined word in a String by another word. This is my code so far, I know I can't do this for now:
treat :: String -> String
treat text = text
main::IO()
main = do argv <- getArgs
texte <- readFile "intputText"
print (separation text)
print ( treat text )
separation :: String -> [String]
separation [] = [""]
separation (c:cs) | c == "\Graph" = "Graphic : " : rest
| c == '}' = "" : rest
| c == '{' = "" : rest
| otherwise = (c : head rest) : tail rest
where rest = separation cs
So basically I know I can't put a String in the first c == "\Graph" so I want to know
how I can basically replace every word "\Graph" in my String text by "Graphic".
I want to be able to do that without importing any package.
If anyone can help me out I'd really appreciate it.
Thank you very much!
replace :: String -> String -> String-> String
replace [] token repl = []
replace str#(s:ss) token#(t:tx) repl
-- check if first char of string equal to first char of token
| s == t = case validateToken token str of
Just list -> repl ++ replace list token repl
Nothing -> s : replace ss token repl
-- if not equal then continue recursion step
| otherwise = s: replace ss token repl
where
-- validate if token matches the following chars of the string
-- returns Nothing if token is not matched
-- returns the remaining string after the token if token is matched
validateToken:: String -> String -> Maybe String
validateToken (a:as) [] = Nothing
validateToken [] list = Just list
validateToken (a:as) (x:xs)
| a == x = validateToken as xs
| otherwise = Nothing
example = replace "yourString" "token" "new"

Pattern matching failing for valid pattern

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.

Haskell - Decode message with pattern

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

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.

Improve a haskell script

I'm a newbie in Haskell and I'd like some opinions about improving this script. This is a code generator and requires a command line argument to generate the sql script.
./GenCode "people name:string age:integer"
Code:
import Data.List
import System.Environment (getArgs)
create_table :: String -> String
create_table str = "CREATE TABLE " ++ h (words str)
where h (x:xs) = let cab = x
final = xs
in x ++ "( " ++ create_fields xs ++ ")"
create_fields (x:xs) = takeWhile (/=':') x ++ type x ++ sig
where sig | length xs > 0 = "," ++ create_fields xs
| otherwise = " " ++ create_fields xs
create_fields [] = ""
type x | isInfixOf "string" x = " CHARACTER VARYING"
| isInfixOf "integer" x = " INTEGER"
| isInfixOf "date" x = " DATE"
| isInfixOf "serial" x = " SERIAL"
| otherwise = ""
main = mainWith
where mainWith = do
args <- getArgs
case args of
[] -> putStrLn $ "You need one argument"
(x:xs) -> putStrLn $ (create_table x)
I think you understand how to write functional code already. Here are some small style notes:
Haskell usually uses camelCase, not under_score_separation
In create_table, cabo and final are not used.
Usually a list-recursive function like create_fields puts the empty list case first.
I would not make create_fields recursive anyway. The comma-joining code is quite complicated and should be separated from the typing code. Instead do something like Data.List.intercalate "," (map create_field xs). Then create_field x can just be takeWhile (/=':') x ++ type x
Especially if there are a lot of types to be translated, you might put them into a map
Like so:
types = Data.Map.fromList [("string", "CHARACTER VARYING")
,("integer", "INTEGER")
-- etc
]
Then type can be Data.Maybe.fromMaybe "" (Data.Map.lookup x types)
Code can appear in any order, so it's nice to have main up front. (This is personal preference though)
You don't need mainWith.
Just say
main = do
args <- getArgs
case args of
[] -> ...
You don't need the dollar for the calls to putStrLn. In the first call, the argument wouldn't require parentheses anyway, and in the second, you supply the parentheses. Alternatively, you could keep the second dollar and drop the parentheses.
Don't use length xs > 0 (in sig); it needlessly counts the length of xs when all you really wanted to know is whether it's empty. Use null xs to check for a non-empty list:
...
where sig | null xs = ... -- Empty case
| otherwise = ... -- Non-empty case
or add an argument to sig and pattern match:
...
where sig (y:ys) = ...
sig [] = ...
Although Nathan Sanders' advice to replace the whole recursive thing with intercalate is excellent and makes this a moot point.
You're also identifying the type by passing the whole "var:type" string into type, so it is testing
"string" `isInfixOf` "name:string"
etc.
You could use break or span instead of takeWhile to separate the name and type earlier:
create_fields (x:xs) = xname ++ type xtype ++ sig
where
(xname, _:xtype) = break (==':') x
sig = ...
and then type can compare for string equality, or look up values using a Map.
A quick explanation of that use of break:
break (==':') "name:string" == ("name", ":string")
Then when binding
(xname, _:xtype) to ("name", ":string"),
xname -> "name"
_ -> ':' (discarded)
xtype -> "string"

Resources