haskell split function for multi-character delimeter - haskell

I've seen this code for the split function in Haskell already:
split :: String -> [String]
split [] = [""]
split (c:cs)
| c == ',' = "" : (split cs)
| otherwise = (c : head (split cs)) : tail (split cs)
Sample: Input: "1,2,3" Output:["1","2","3"]
However, this code only works when the delimeter is a ','. The code could be changed easily like this:
split :: Char->String -> [String]
split [] = [""]
split d (c:cs)
| c == d = "" : (split cs)
| otherwise = (c : head (split cs)) : tail (split cs)
So that the function works for any single character as the delimeter.
However, I want a function that works when the delimeter is a string itself.
So, I've tried this:
split' :: String -> String -> [String]
split' (d:ds) (c:cs)
| (d:ds) `isPrefixOf'` (c:cs) = "":rest
| otherwise = (c:head(split'((d:ds),rest))):tail(split'((d:ds),rest))
where
rest = split' (d:ds) drop( length(d:ds) (c:cs) )
where
isPrefixOf' :: (Eq a) => [a] -> [a] -> Bool
isPrefixOf' [] _ = True
isPrefixOf' _ [] = False
isPrefixOf' (x:xs) (y:ys)= (x == y) && (isPrefixOf' xs ys)
However, I get this ugly error when I load the program:
Prelude> :l testingsplit.hs
[1 of 1] Compiling Main ( testingsplit.hs, interpreted )
testingsplit.hs:15:27: error:
• Couldn't match expected type ‘[[Char]]’
with actual type ‘String -> [String]’
• Probable cause: ‘split'’ is applied to too few arguments
In the first argument of ‘head’, namely ‘(split' ((d : ds), rest))’
In the second argument of ‘(:)’, namely
‘head (split' ((d : ds), rest))’
In the first argument of ‘(:)’, namely
‘(c : head (split' ((d : ds), rest)))’
|
15 | | otherwise = (c:head(split'((d:ds),rest))):tail(split'((d:ds),rest))
| ^^^^^^^^^^^^^^^^^^^
testingsplit.hs:15:33: error:
• Couldn't match type ‘([Char], b0)’ with ‘[Char]’
Expected type: String
Actual type: ([Char], b0)
• In the first argument of ‘split'’, namely ‘((d : ds), rest)’
In the first argument of ‘head’, namely ‘(split' ((d : ds), rest))’
In the second argument of ‘(:)’, namely
‘head (split' ((d : ds), rest))’
|
15 | | otherwise = (c:head(split'((d:ds),rest))):tail(split'((d:ds),rest))
| ^^^^^^^^^^^^^
testingsplit.hs:15:54: error:
• Couldn't match expected type ‘[[Char]]’
with actual type ‘String -> [String]’
• Probable cause: ‘split'’ is applied to too few arguments
In the first argument of ‘tail’, namely ‘(split' ((d : ds), rest))’
In the second argument of ‘(:)’, namely
‘tail (split' ((d : ds), rest))’
In the expression:
(c : head (split' ((d : ds), rest)))
: tail (split' ((d : ds), rest))
|
15 | | otherwise = (c:head(split'((d:ds),rest))):tail(split'((d:ds),rest))
| ^^^^^^^^^^^^^^^^^^^
testingsplit.hs:15:60: error:
• Couldn't match type ‘([Char], b1)’ with ‘[Char]’
Expected type: String
Actual type: ([Char], b1)
• In the first argument of ‘split'’, namely ‘((d : ds), rest)’
In the first argument of ‘tail’, namely ‘(split' ((d : ds), rest))’
In the second argument of ‘(:)’, namely
‘tail (split' ((d : ds), rest))’
|
15 | | otherwise = (c:head(split'((d:ds),rest))):tail(split'((d:ds),rest))
| ^^^^^^^^^^^^^
testingsplit.hs:17:13: error:
• Couldn't match expected type ‘t0 -> t’
with actual type ‘[String]’
• The function ‘split'’ is applied to three arguments,
but its type ‘String -> String -> [String]’ has only two
In the expression: split' (d : ds) drop (length (d : ds) (c : cs))
In an equation for ‘rest’:
rest = split' (d : ds) drop (length (d : ds) (c : cs))
• Relevant bindings include
rest :: t (bound at testingsplit.hs:17:6)
|
17 | rest = split' (d:ds) drop( length(d:ds) (c:cs) )
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
testingsplit.hs:17:27: error:
• Couldn't match type ‘Int -> [a0] -> [a0]’ with ‘[Char]’
Expected type: String
Actual type: Int -> [a0] -> [a0]
• Probable cause: ‘drop’ is applied to too few arguments
In the second argument of ‘split'’, namely ‘drop’
In the expression: split' (d : ds) drop (length (d : ds) (c : cs))
In an equation for ‘rest’:
rest = split' (d : ds) drop (length (d : ds) (c : cs))
|
17 | rest = split' (d:ds) drop( length(d:ds) (c:cs) )
| ^^^^
testingsplit.hs:17:33: error:
• Couldn't match expected type ‘[Char] -> t0’
with actual type ‘Int’
• The function ‘length’ is applied to two arguments,
but its type ‘[Char] -> Int’ has only one
In the third argument of ‘split'’, namely
‘(length (d : ds) (c : cs))’
In the expression: split' (d : ds) drop (length (d : ds) (c : cs))
|
17 | rest = split' (d:ds) drop( length(d:ds) (c:cs) )
| ^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
I don't understand where I have gone wrong.
Also, I already know Data.List has the splitOn method, which does this already. However, I am not allowed to use that module for the work I am doing. I am only interested in answers that only use Haskell's Data.Char and the Prelude.
I have a follow-up question: How could I change split' so it works for any type of list in general. I'm not fully sure what type signature I should use.
I've tried this:
split'' :: Eq a => [a] -> [a] -> [[a]]
split'' _ [] = []
split'' [] x = map (:[]) x
split'' a#(d:ds) b#(c:cs)
| Just suffix <- a `stripPrefix'` b = "":split'' a suffix
| otherwise = if null rest
then [[c]]
else (c:head rest):tail rest
where
rest = split'' a $ tail b
stripPrefix' :: Eq a => [a] -> [a] -> Maybe [a]
stripPrefix' [] ys = Just ys
stripPrefix' (x:xs) (y:ys)
| x == y = stripPrefix' xs ys
stripPrefix' _ _ = Nothing
Now, I get this error about the Char type when I load the program:
[1 of 1] Compiling Main ( splittest.hs, interpreted )
splittest.hs:24:46: error:
• Couldn't match type ‘a’ with ‘Char’
‘a’ is a rigid type variable bound by
the type signature for:
split'' :: forall a. Eq a => [a] -> [a] -> [[a]]
at splittest.hs:20:1-38
Expected type: [[Char]]
Actual type: [[a]]
• In the second argument of ‘(:)’, namely ‘split'' a suffix’
In the expression: "" : split'' a suffix
In an equation for ‘split''’:
split'' a#(d : ds) b#(c : cs)
| Just suffix <- a `stripPrefix'` b = "" : split'' a suffix
| otherwise
= if null rest then [[c]] else (c : head rest) : tail rest
where
rest = split'' a $ tail b
• Relevant bindings include
suffix :: [a] (bound at splittest.hs:24:12)
rest :: [[a]] (bound at splittest.hs:29:7)
cs :: [a] (bound at splittest.hs:23:23)
c :: a (bound at splittest.hs:23:21)
b :: [a] (bound at splittest.hs:23:18)
ds :: [a] (bound at splittest.hs:23:14)
(Some bindings suppressed; use -fmax-relevant-binds=N or -fno-max-relevant-binds)
|
24 | | Just suffix <- a `stripPrefix'` b = "":split'' a suffix
| ^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
I'm new to Haskell, so I don't know where I have made a mistake.
Also, thank you for your earlier reply. It was very helpful and detailed.

Your code has many things wrong with it, including bad spacing and repetition of (d:ds) and (c:cs) when you could just give the whole thing a name by taking a#(d:ds) as an argument. However, these are all just stylistic errors. The biggest error in your code is the fact that you're trying to call functions like python, where it's the function name followed by parentheses followed by a comma separated list of arguments. Haskell does not work like this. Function arguments are separated by spaces, and you don't need parentheses after the function call. If you comma-separate arguments, you just create a tuple and call the function on that tuple, which breaks the whole thing. Here's your cleaned up code:
split' :: String -> String -> [String]
split' a#(d:ds) b#(c:cs)
| a `isPrefixOf'` b = "":rest
| otherwise = (c:head (split' a rest)):tail (split' a rest)
where
rest = split' a $ drop (length a) b
Isn't this much nicer to read? Unfortunately, this still doesn't work. rest already has split' a called on it, so you don't need to call split' a on it again in the body of your function:
split' :: String -> String -> [String]
split' a#(d:ds) b#(c:cs)
| a `isPrefixOf'` b = "":rest
| otherwise = (c:head rest):tail rest
where
rest = split' a $ drop (length a) b
Now, this compiles, but it still doesn't work:
λ> split' ", " "1, 2, 3"
["1 ","3*** Exception: Non-exhaustive patterns in function split'
This would be indicative of the fact that we have to add our base cases:
split' :: String -> String -> [String]
-- splitting nothing is nothing
split _ [] = []
-- splitting on empty string just returns array of individual chars
split [] x = map (:[]) x
split' a#(d:ds) b#(c:cs)
| a `isPrefixOf'` b = "":rest
| otherwise = (c:head rest):tail rest
where
rest = split' a $ drop (length a) b
Now, this compiles, but still has non-exhaustive patterns:
λ> split' ", " "1, 2, 3"
["1 ","3*** Exception: Non-exhaustive patterns in function split'
This time, it's because rest could be an empty list. So, we should check for that:
split' :: String -> String -> [String]
split _ [] = []
split [] x = map (:[]) x
split' a#(d:ds) b#(c:cs)
| a `isPrefixOf'` b = "":rest
| otherwise = if null rest
-- if rest is empty we default to just the one character
then [[c]]
else (c:head rest):tail rest
where
rest = split' a $ drop (length a) b
This still doesn't work, since we're removing the length of the delimiter every time, but we really only want to do that if we find a match. If we don't find a match, we should only drop one character.
The final working code is:
split' :: String -> String -> [String]
split' _ [] = []
split' [] x = map (:[]) x
split' a#(d:ds) b#(c:cs)
| a `isPrefixOf'` b = "":(split' a $ drop (length a) b)
| otherwise = if null rest
then [[c]]
else (c:head rest):tail rest
where
rest = split' a $ tail b
As a side note, your code can be cleaned up by using stripPrefix in Data.List instead of isPrefixOf', which allows you to save a drop and avoid iterating over the list twice:
import Data.List
split' :: String -> String -> [String]
split' _ [] = []
split' [] x = map (:[]) x
split' a#(d:ds) b#(c:cs)
| Just suffix <- a `stripPrefix` b = "":split' a suffix
| otherwise = if null rest
then [[c]]
else (c:head rest):tail rest
where
rest = split' a $ tail b
It's also pretty easy to implement yourself:
stripPrefix :: Eq a => [a] -> [a] -> Maybe [a]
stripPrefix [] ys = Just ys
stripPrefix (x:xs) (y:ys)
| x == y = stripPrefix xs ys
stripPrefix _ _ = Nothing
Here's the generic version:
import Data.List
split' :: (Eq a) => [a] -> [a] -> [[a]]
split' _ [] = []
split' [] x = map (:[]) x
split' a#(d:ds) b#(c:cs)
| Just suffix <- a `stripPrefix` b = []:split' a suffix
| otherwise = if null rest
then [[c]]
else (c:head rest):tail rest
where
rest = split' a $ tail b

Related

Couldn't match expected type ‘[Int]’ with actual type ‘Int’ haskell

I'm trying to do an exercise that gives a list of every path of a binary tree from leaf to root
here's my code:
data Tree = Leaf Int | Node (Tree) Int (Tree)
go (Leaf a) = (a : []) : []
go (Node l n r) = insev((go l):(go r)) n
insev :: [[a]] -> a -> [[a]]
insev [[]] x = []
insev (h:t) x = (insb h x) : (insev t x)
insb [] num = num : []
insb (h:t) num = h:(insb t num)
it should be correct from logical perspective but i am new to haskell and i don't know why i get this error
Main.hs:21:19: error:
• Couldn't match type ‘[Int]’ with ‘Int’
Expected: [[Int]]
Actual: [[[Int]]]
• In the expression: insev ((go l) : (go r)) n
In an equation for ‘go’:
go (Node l n r) = insev ((go l) : (go r)) n
|
21 | go (Node l n r) = insev ((go l):(go r)) n
| ^^^^^^^^^^^^^^^^^^^^^^^
Main.hs:21:34: error:
• Couldn't match type ‘Int’ with ‘[Int]’
Expected: [[[Int]]]
Actual: [[Int]]
• In the second argument of ‘(:)’, namely ‘(go r)’
In the first argument of ‘insev’, namely ‘((go l) : (go r))’
In the expression: insev ((go l) : (go r)) n
|
21 | go (Node l n r) = insev ((go l):(go r)) n
| ^^^^
Main.hs:21:41: error:
• Couldn't match expected type ‘[Int]’ with actual type ‘Int’
• In the second argument of ‘insev’, namely ‘n’
In the expression: insev ((go l) : (go r)) n
In an equation for ‘go’:
go (Node l n r) = insev ((go l) : (go r)) n
|
21 | go (Node l n r) = insev ((go l):(go r)) n
Without even looking at the rest of the code, this is never* going to fly:
(go l) : (go r)
Presumably go is returning something of the same type in both calls, but the arguments to (:) must have different types:
(:) :: a -> [a] -> [a]
Perhaps you want (++) instead, whose arguments must have the same types:
(++) :: [a] -> [a] -> [a]
* Okay, yes, it is possible to write go in a way that invalidates the next sentence's assumption that it is returning the same type in both calls. But that's a pretty unusual situation to be in, and one you have to pretty consciously try to arrive at.

Couldn't match expected type [Int] Haskell

I am new to Haskell so I don't quite understand most of its errors. I have encountered an error trying to make a higher order function that uses foldl() to read a list and then multiply it by 2 (it automatically reverses it) and I use another another foldl() just to read it, in so reversing it to its original order.
I would be thankful for any help I receive.
here's the error
• Couldn't match expected type ‘[Int]’
with actual type ‘t0 Integer -> [Integer]’
• Probable cause: ‘foldl’ is applied to too few arguments
In the first argument of ‘reverses’, namely
‘(foldl (\ acc x -> (2 * x) : acc) [])’
In the expression: reverses (foldl (\ acc x -> (2 * x) : acc) [])
In an equation for ‘multthr’:
multthr = reverses (foldl (\ acc x -> (2 * x) : acc) [])
|
16 | multthr = reverses(foldl(\acc x-> (2*x):acc)[])
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
here's the source code
reverses::[Int] ->[Int]
reverses = foldl(\acc x-> x:acc)[]
multthr::[Int]->([Int]->[Int])
multthr = reverses(foldl(\acc x-> (2*x):acc)[])
You need to compose your two foldl's with (.), instead of applying one to the other:
reverses :: [a] -> [a]
reverses = foldl (\acc x-> x:acc) []
---- multthr :: [Int] -> ([Int] -> [Int]) -- why??
multthr :: [Int] -> [Int]
---- multthr = reverses (foldl(\acc x-> (2*x):acc)[]) -- causes error
multthr = reverses . foldl (\acc x-> (2*x):acc) []
so that we have
> multthr [1..5]
[2,4,6,8,10]
it :: [Int]
(.) is the functional composition, defined as (f . g) x = f (g x).
Instead of doing the two foldl's you can do one foldr,
mult2 = foldr (\x r -> (2*x) : r) []
but then this is nothing else but, simply, map (2*).

Function does not work when I call it in an other function

import Data.List (sort,nub,(\\),transpose,genericLength)
import Data.String (lines,unlines)
type Row a = [a]
type Matrix a = [Row a]
type Digit = Char
digits :: [Digit]
digits = ['1'..'9']
blank :: Digit -> Bool
blank d = d == ' '
-- 1.
group :: [a] -> [[a]]
group = groupBy 3
groupBy :: Int -> [a] -> [[a]]
groupBy _ [] = []
groupBy n xs = let (ys, zs) = splitAt n xs
in ys : groupBy n zs
-- 2.
intersperse :: a -> [a] -> [a]
intersperse x y = x : [ x | ys <- y, x <- [ys, x]]
-- 3.
showRow :: String -> String
showRow xs = intersperse "|" (groupBy 3 xs)
Error Message Below
Tutorial9.hs:35:14: error:
• Couldn't match type ‘[Char]’ with ‘Char’
Expected type: String
Actual type: [[Char]]
• In the expression: intersperse "|" (groupBy 3 xs)
In an equation for ‘showRow’:
showRow xs = intersperse "|" (groupBy 3 xs)
|
35 | showRow xs = intersperse "|" (groupBy 3 xs)
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The group function works well, but doesn't seem to work when I call it in showRow. Why?
The intersperse :: a -> [a] -> [a]** will return a [String] not a String, since groupBy 3 xs will return [String] and the separator is a String. You can make use of concat to concatenate the strings together:
showRow :: String -> String
showRow xs = concat (intersperse "|" (groupBy 3 xs))
or you can make use of intercalate :: [a] -> [[a]] -> [a], which does this already:
import Data.List(intercalate)
showRow :: String -> String
showRow xs = intercalate "|" (groupBy 3 xs)

Haskell type mismatch Int and [Int]

I'm a beginner to Haskell. I'm trying to create a function which has two parameters: a character and a string.
This function is supposed to go through the string and check if the character given is in the string, and then return a list of integers representing the position of the characters in the string.
My code is:
tegnPose :: Char -> String -> [Int]
tegnPose c [] = []
tegnPose c (x:xs) = [if not (xs !! a == c)
then [a] ++ tegnPose c xs
else tegnPose c xs |a <- [0.. length xs - 1]]
Which is a recursive function with list comprehension.
The error I get:
Uke4.hs:14:7: error:
* Couldn't match expected type `Int' with actual type `[Int]'
* In the expression: [a] ++ tegnPose c xs
In the expression:
if not (xs !! a == c) then [a] ++ tegnPose c xs else tegnPose c xs
In the expression:
[if not (xs !! a == c) then
[a] ++ tegnPose c xs
else
tegnPose c xs |
a <- [0 .. length xs - 1]]
|
14 | then [a] ++ tegnPose c xs
| ^^^^^^^^^^^^^^^^^^^^
Uke4.hs:15:7: error:
* Couldn't match expected type `Int' with actual type `[Int]'
* In the expression: tegnPose c xs
In the expression:
if not (xs !! a == c) then [a] ++ tegnPose c xs else tegnPose c xs
In the expression:
[if not (xs !! a == c) then
[a] ++ tegnPose c xs
else
tegnPose c xs |
a <- [0 .. length xs - 1]]
|
15 | else tegnPose c xs |a <- [0.. length xs - 1]]
I don't understand how the mismatch happens, as the recursive function should just run through.
Here's why the mismatch happens. First, note that a list comprehension that returns a list of type [a] must generate elements of type a, so you need the following to match:
example :: [Int]
-- .-- the final value is "[Int]"
-- |
example = [ 2+x*y | x <- [1..10], y <- [1..5], x < y]
-- ^^^^^
-- |
-- `- therefore, this must be "Int"
In your example, the type signature for tegnPose implies that the list comprehension must return an [Int], but the expression generating list elements, namely:
if ... then [a] ++ tegnPose c xs else tegnPose c cx
is clearly not returning a plain Int the way it's supposed to.
The first error message is indicating that actual type of the subexpression [a] ++ tegnPos c xs which is [Int] does not match the expected type of the result of the entire if .. then .. else expression which should have type Int.
If I understand your question correctly (i.e., return a list of the integer positions of each occurrence of a character in a string so that tegnPose 'a' "abracadabra" returns [0,3,5,7,10], then you should either use recursion or a list comprehension, but not both.
Note that the non-recursive list comprehension:
tegnPose c xs = [a | a <- [0..length xs - 1]
almost does what you want. All that's missing is testing the condition to see if the character at position a is a c. If you don't know about using "guards" in list comprehensions, go look it up.
Alternatively, the recursive function without a list comprehension:
tegnPose c (x:xs) = if (x == c) then ??? : tegnPose c xs
else tegnPose c xs
tegnPose _ [] = []
also almost does what you want, except it's not obvious what to put in place of ??? to return a number indicating the current position. If you write a recursive version with an extra parameter:
tp n c (x:xs) = if (x == c) then n : tp (???) c xs
else tp (???) c xs
tp _ _ [] = []
with the idea that you could define:
tegnPose c xs = tp 0 c xs
then you'd be closer, if only you could figure out what new value for n should go in place of the ???.
More standard Haskell solutions might involve things like zips:
> zip [0..] "abracadabra"
[(0,'a'),(1,'b'),(2,'r'),...]
and filters:
> filter (\(i,c) -> c == 'a') $ zip [0..] "abracadabra"
[(0,'a'),(3,'a'),...]
and maps:
> map fst $ filter (\(i,c) -> c == 'a') $ zip [0..] "abracadabra"
[0,3,5,7,10]
or looking in Data.List for a function that does what you want:
> elemIndices 'a' "abracadabra"
[0,3,5,7,10]
Just for some variety a simpler way of implementing this functionality with a single foldr could be;
import Data.Bool (bool)
charIndices :: Char -> String -> [Int]
charIndices c = foldr (\t r -> bool r (fst t : r) (snd t == c)) [] . zip [0..]
*Main> charIndices 't' "tektronix test and measurement instruments"
[0,3,10,13,29,34,40]
Explanation:
Type of foldr is Foldable t => (a -> b -> b) -> b -> t a -> b
It takes three parameters;
A function which accepts two parameters
An initial value of type b
A traversable data type which hold values of type a
an returns a single value of type b.
In this particular case our type a value is Char type, which makes t a a String type (due to type signature) and type b value is a list of integers [Int].
The provided function as the first parameter is (\t r -> bool r (fst t : r) (snd t == c)) which is very simple if you check Data.bool. bool is a ternary operator of type a -> a -> Bool -> a which takes three arguments. In order they are negative result, positive result and condition. (negative is on the left as usual in Haskell). It checks if the current character is equal to our target character c, if so it returns fst t : r if not r (r means result). And finally t is the current tuple of the fed tuples list. The tuples list is constructed by zip [0..] s where s is not shown in the function definition due to partial application.

Haskell String to Maybe List

readSquareTransition :: String -> Maybe [SquareTurn]
readSquareTransition [] = Just []
readSquareTransition (x:xs) = case x of
'L' -> Just (L : readSquareTransition xs)
'R' -> Just (R : readSquareTransition xs)
_ -> Nothing
I want to get Just [L,L,R,R]. But looks like i failed :( Here is the error message!
src/StudentSources/LangtonsAnt.hs:231:24:
Couldn't match expected type ‘[SquareTurn]’
with actual type ‘Maybe [SquareTurn]’
In the second argument of ‘(:)’, namely ‘readSquareTransition xs’
In the first argument of ‘Just’, namely
‘(L : readSquareTransition xs)’
src/StudentSources/LangtonsAnt.hs:232:24:
Couldn't match expected type ‘[SquareTurn]’
with actual type ‘Maybe [SquareTurn]’
In the second argument of ‘(:)’, namely ‘readSquareTransition xs’
In the first argument of ‘Just’, namely
‘(R : readSquareTransition xs)’
A modular way of doing this would be to define readSquareTurn first which defines how to turn a Char into a single SquareTurn (with the possibility of failure):
readSquareTurn :: Char -> Maybe SquareTurn
readSquareTurn x = case x of
'L' -> Just L
'R' -> Just R
_ -> Nothing
and then use mapM :: (a -> Maybe b) -> [a] -> Maybe [b] to process the whole String like so:
readSquareTransition :: String -> Maybe [SquareTurn]
readSquareTransition = mapM readSquareTurn
Change this
'L' -> Just (L : readSquareTransition xs)
'R' -> Just (R : readSquareTransition xs)
to this
'L' -> fmap (L :) $ readSquareTransition xs
'R' -> fmap (R :) $ readSquareTransition xs
The problem is that readSquareTransition returns a Maybe [SquareTurn], so you can't apply (:) to it ((:) requires a List). fmap however lets you apply into the Just (while preserving a Nothing).

Resources