Reputation: 97
I'm trying to make a function in Haskell to split a string at a certain char
and a list at a certain number.
For doing this the splitAt function is exactly what I need for numbers, but I can't give a char with this function.
E.g.
splitAt 5 [1,2,3,4,5,6,7,8,9,10]
gives
([1,2,3,4,5],[6,7,8,9,10])
that is exactly what I needed with the 5 in the left side of the tuple.
But now I want to do this with a char
and a string. But splitAt
only takes and int
for the second argument. I want
splitAt 'c' "abcde"
resulting in
("abc", "de")
I looking for something in the direction of
splitAt (findIndex 'c' "abcde") "abcde"
but the function findIndex
returns something of the type Maybe Int
and splitAt
needs an Int
. Then I tried the following
splitAt (head (findIndices (== 'c') "abcde")) "abcde"
This is a possible solution but it returns the following
("ab","cde")
with the c on the wrong side of the tupple. You can add succ
to c but what will the result be if the char is a Z.
Is there an easy way to modify to make
splitAt (findIndex 'c' "abcde") "abcde"
work?
Upvotes: 3
Views: 1259
Reputation: 47042
Here's a different way, that doesn't involve messing around with list indices.
break
is nearly what you want. Let's reuse it. You want the matching element to be included at the end of the first output list, instead of at the start of the second.
import Control.Arrow ((***))
breakAfter :: (a -> Bool) -> [a] -> ([a], [a])
breakAfter p xs = map fst *** map fst $ break snd (zip xs $ False : map p xs)
How this works:
zip
). The first element of each pair is taken from the original list. The second element of the pair is a Bool
stating whether the previous element of the list is the one we're looking for. This is why we say False : map p xs
--- if we just said map p xs
, we would reproduce exactly the behaviour of break
. Sticking the extra False
in at the start is the important bit.break
. Our condition is encoded in the second element of each pair.Bool
s. We don't need them any more.Upvotes: 0
Reputation: 51393
You can use the fromMaybe
function to take the result from Maybe, for example:
splitlist = splitAt (fromMaybe 0 (findIndex 'c' "abcde") "abcde")
fromMaybe :: a -> Maybe a -> a
The fromMaybe function takes a default value and Maybe value. If the Maybe is Nothing, it returns the default values; otherwise, it returns the value contained in the Maybe. (source).
With the default value set to 0, if your findIndex return Nothing
the result of splitAt will be ("",list)
, for the same case but with default value set to length list
the finally result it will be (list,"")
.
Upvotes: 2
Reputation: 9566
Given c :: Char
and s :: String
, you can write some as
splitAt ((1+) $ fromJust $ findIndex (==c) s) s
but
c
is not into s
s
two timesA Maybe
alternative is
maybe Nothing (\x -> splitAt (1+x) s) (findIndex (==c) s)
you can set "else value" (Nothing
in my example).
You can write your own function as
splitAt' :: Char -> String -> (String, String)
splitAt' _ [] = ("", "")
splitAt' c (x:xs) | c == x = ([c], xs)
| True = (x:cs, ys) where (cs, ys) = splitAt' c xs
Then, you get (s, "")
if not c
in s
.
Upvotes: 1
Reputation: 32455
You can use findIndex
, just unwrap the Maybe
and add one:
import Data.List
splitAfter :: (a-> Bool) -> [a] -> ([a],[a])
splitAfter this xs = case findIndex this xs of
Nothing -> (xs,[])
Just n -> splitAt (n+1) xs
giving, for example
*Main> splitAfter (=='c') "abcde"
("abc","de")
Maybe is a handy datatype for encoding failure in a way that's easy to recover. There's even a function maybe :: b -> (a -> b) -> Maybe a -> b
to use a default value and a function to handle the two cases separately:
splitAfter' :: (a-> Bool) -> [a] -> ([a],[a])
splitAfter' this xs = maybe (xs,[])
(\n -> splitAt (n+1) xs)
(findIndex this xs)
which also works. For example
*Main> splitAfter' (==5) [1..10]
([1,2,3,4,5],[6,7,8,9,10])
Upvotes: 2