I know the Data.List module comes with the predefined function groupBy which i want to use to split a string into groups of consecutive vowels and non-vowels. the format of the function groupBy is as follows:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
How can I use this format to do the splitting for a string?
Thanks
Like this
ghci> import Data.Char
ghci> import Data.List
ghci> groupBy (const isAlphaNum) "A bunch of words and numbers34"
["A"," bunch"," of"," words"," and"," numbers34"]
Or
ghci> groupBy (const isAlpha) "A bunch of words and numbers34"
["A"," bunch"," of"," words"," and"," numbers","3","4"]
Edit: Since there has been no indication that a solution has been found to the extended problem, in the interest of keeping up the standard of SO I shall complete the answer to the problem:
import Data.List
isVowel :: Char -> Bool
isVowel c = c `elem` "aeiouy"
bothVowelConsonant :: Char -> Char -> Bool
bothVowelConsonant a b = all isVowel [a,b] || not (any isVowel [a,b])
splitByVowel :: String -> [String]
splitByVowel s = groupBy bothVowelConsonant s
Related
Can I get a quick explanation on how I would concatenate a list of strings into one string using map? I was trying to use intercalate but I realized that this is used to combine lists and not strings. i.e. [[char]] instead of [String]
type InformationList = (String , [String] )
concatenateList :: String -> [InformationList] -> String
concatenateList n cs = do
let [informationlist] = intercalate " " cs
let toWrite = n ++ [informationList]
return toWrite
I updated this so that you can see the code I've been working with. It is saying that [[char]] is not the same as [informationlist] but that should be the same as [String]
A String is a list of Chars. A String is a type alias defined as:
type String = [Char]
If you want to concatenate a list of Strings without any separators, you can use concat :: Foldable t => t [a] -> [a]:
Prelude> concat ["foo", "bar", "qux"]
"foobarqux"
In case you want to insert a string as delimiter, you can use intercalate :: [a] -> [[a]] -> [a]:
Prelude> import Data.List(intercalate)
Prelude Data.List> intercalate "," ["foo","bar","qux"]
"foo,bar,qux"
I write a code to convert a string to be a list of Intger:
convert::String -> Maybe [Int]
convert (c:cs)
isDigit c = Just(c: (convert cs):[])
otherwise = Nothing
and it shows an error...
test.hs:15:26: error:
parse error on input ‘=’
Perhaps you need a 'let' in a 'do' block?
e.g. 'let x = 5' instead of 'x = 5'
Why is that so...
While there are other compile errors in your code, the reason you're getting the error message about the parse error is because you are not including the pipe character used in guards.
convert (c:cs)
| isDigit c = Just(c: (convert cs):[])
| otherwise = Nothing
There were several errors in your code. You need to try to convert each character, which gives a Maybe Int. Then you loop on the string using mapM inside the Maybe monad :
import Data.Char
convertChar :: Char -> Maybe Int
convertChar c = if isDigit c then Just $ ord c else Nothing
convert :: String -> Maybe [Int]
convert = mapM convertChar
Another way of looking at V. Semeria's answer is to use sequence (from the Traversable type class) with map:
import Data.Char -- for ord
convertChar :: Char -> Maybe Int
convertChar c = if isDigit c then Just $ ord c - 48 else Nothing
convert :: String -> Maybe [Int]
-- Start with String -> [Maybe Int], then convert
-- the [Maybe Int] to Maybe [Int]
convert = sequence . map convertChar
This is a common pattern, so Traversable also provides traverse, which in this case is equivalent to sequence . map.
convert :: Traversable t => t Char -> Maybe (t Int)
convert = traverse converChar
Some examples (most of the instances of Traversable available by default aren't very interesting, but various tree types can have instances).
> convert "123"
Just [1,2,3]
> convert (Just '1')
Just (Just 1)
> convert (True, '2')
Just (True, 2)
> convert (Right '1')
Just (Right 1)
I want to write a function that gets the first integer in a string, if the integer is at the beginning of the string and is followed by space or nothing.
For example, "12" and "12 kids" would be valid strings, but "12kids", "a12" are invalid.
This is my function:
getFirstInteger :: String -> Int
getFirstInteger [] = error "No integer"
getFirstInteger str
| dropWhile (Char.isNumber) str == [] = read str :: Int
| Char.isSpace $ head $ dropWhile (Char.isNumber) str = read (takeWhile (Char.isNumber) str) :: Int
| otherwise = error "No integer found"
The problem is that if the string is actually an integer, head will raise an exception, so that is why the first condition exists. Is there any elegant solution to this problem?
getFirstInteger :: String -> Int
getFirstInteger = read . head . words
words will split the String into a list of Strings, which were originally separated by whitespace. head will take the first (or error if the original string was empty), and read will parse the string as usual (and error if the first word wasn't a valid Int).
However, I prefer a variant that doesn't use error on empty Strings or unparseable ones, e.g.
import Text.Read (readMaybe)
getFirstInteger :: String -> Maybe Int
getFirstInteger [] = Nothing
getFirstInteger xs = readMaybe . head . words $ xs
One could write this completely point-free with listToMaybe from Data.Maybe, but that's probably an overkill:
import Data.Maybe (listToMaybe)
import Text.Read (readMaybe)
import Control.Monad ((>=>))
getFirstInteger :: String -> Maybe Int
getFirstInteger = listToMaybe . words >=> readMaybe
If you want to parse strings without using parser combinator libraries or any of the machinery in Text.Read, have a look at the functions break and span:
span :: (a -> Bool) -> [a] -> ([a], [a])
break :: (a -> Bool) -> [a] -> ([a], [a])
The nice thing is that both of these functions not only return what they match but also the remainder of the string allowing you to continue your parsing.
To solve your problem:
import Data.Char
getFirstInteger :: String -> Maybe Int
getFirstInteger str
let (digs, rest1) = span isDigit str
endsok = case rest1 of
[] -> True
(c:_) -> c == ' '
in
if not (null digs) && endsok
then Just (read digs)
else Nothing -- either no digits or doesn't end properly
This version does not allow a leading minus sign. This next version allows an optional leading minus sign for the integer:
getFirstInteger' str =
let (minus,rest1) = span (=='-') str
(digs, rest2) = span isDigit rest1
endsok = case rest2 of
[] -> True
(c:_) -> c == ' '
in
if length minus <= 1 && not (null digs) && endsok
then Just (read (minus ++ digs))
else Nothing
Yes - this does not terminate as early as possible on bad input. I provide it mainly as an example of how to chain together calls to span and break.
Use reads. For example:
type Unit = String
readUnit :: String -> Maybe (Int, Maybe Unit)
readUnit s = case reads s of -- the integer is at the beginning of the string and...
(n, ' ':unit):_ -> Just (n, Just unit) -- is followed by space...
(n, "" ):_ -> Just (n, Nothing) -- or nothing.
_ -> Nothing
In ghci:
> readUnit "12"
Just (12,Nothing)
> readUnit "12 kids"
Just (12,Just "kids")
> readUnit "12kids"
Nothing
> readUnit "a12"
Nothing
However, there are a few minor considerations to keep in mind. It's possible that read does not restrict the syntax as much as you might want; for example, the following answer may surprise you:
> readUnit " ((-0x5)) kids"
Just (-5,Just "kids")
You may also want to drop extraneous spaces in the unit; for example, you could change the first clause above to
(n, ' ':unit):_ -> Just (n, Just (dropWhile isSpace unit))
or similar. And as a final variation on this theme, note that while the standard instances of Read never return lists with more than one element from reads, it is technically possible that some user-supplied type may do so. So if you were ever to use reads to parse types other than Int, you may want to either demand an unambiguous parse or consider all the parses before deciding what to do; the above code bakes in the assumption that the first parse is as good as any.
From a String like "abc123def45" i want to get -> [123, 45].
I have the following now:
getDecimals :: String -> [Int]
getDecimals xs = [ digitToInt x | x<-xs , isDigit x]
Only this method is returning -> [1,2,3,4,5].
How can i do this?
Tnx!
If you are using Data.List.Split you can do this.
import Data.List.Split
import Data.Char
getDecimals :: String -> [Int]
getDecimals = map read . wordsBy (not . isDigit)
Here is a (quite long) on-liner to do this:
import Data.List
import Data.Char
getDecimals = ((map (read::String->Int)).(filter (isNumber.head)).(groupBy (\a b -> (isNumber a) && (isNumber b))))
Now let's break it down:
(
( map (read::String->Int) ). -- Go over the list of numerical strings and read them as Ints
( filter (isNumber.head) ). -- Filter out the strings which are not begining with digit
( groupBy -- Split the given list based on the following comparison function:
(\a b -> (isNumber a) && (isNumber b)) -- The comparison function to return true if both arguments are digits
)
)
Since we are using the . function, which is function composition, the functions above are applied in the order bottom to top to the given argument list. I am pretty sure there is more elegant ways to do this, but I can't think of any at this moment.
I've never played with regex-applicative before, so I guess I'll give it a go!
NOTE: the cleanest version, but the least interesting, is way down at the bottom.
The first version here is written to use just one regular expression, which means it's not at all lazy (oy!).
import Text.Regex.Applicative
import Data.Char
fakeDigitToInt :: Char -> Int
fakeDigitToInt x = ord x - ord '0'
digit = fakeDigitToInt <$> psym isDigit
addDigit big small = 10*big+small
number = addDigit <$> reFoldl Greedy addDigit 0 digit <*> digit
spacers = many (psym $ not . isDigit)
numbersInCrud = many (spacers *> number) <* spacers
readNumbersFromCrud = maybe [] id . match numbersInCrud
To make a lazy version, it's necessary to break things up a bit more, using more features of the parsing system. While I'm at it, I'll fix up the types a bit:
import Text.Regex.Applicative
import Data.Char
import Data.List (unfoldr)
fakeDigitToInt :: Char -> Int
fakeDigitToInt x = ord x - ord '0'
digit :: Num n => RE Char n
digit = fromIntegral . fakeDigitToInt <$> psym isDigit
addDigit big small = 10*big+small
number :: Num n => RE Char n
number = addDigit <$> reFoldl Greedy addDigit 0 digit <*> digit
spacers = many (psym $ not . isDigit)
pullNumber :: Num n => String -> Maybe (n, String)
pullNumber = findLongestPrefix (spacers *> number)
readNumbersFromCrud :: Num n => String -> [n]
readNumbersFromCrud = unfoldr pullNumber
The cleanest version
This is kind of boring, but it is simple:
import Text.Regex.Applicative
import Text.Regex.Applicative.Common
import Data.Char
import Data.List (unfoldr)
spacers = many (psym $ not . isDigit)
pullNumber :: Num n => String -> Maybe (n, String)
pullNumber = findLongestPrefix (spacers *> decimal)
readNumbersFromCrud :: Num n => String -> [n]
readNumbersFromCrud = unfoldr pullNumber
And because I couldn't resist, here's the (almost) one-liner:
import Text.Regex.Applicative
import Text.Regex.Applicative.Common
import Data.Char
import Data.List (unfoldr)
readNumbersFromCrud :: Num n => String -> [n]
readNumbersFromCrud = unfoldr . findLongestPrefix $
many (psym $ not . isDigit) *> decimal
I am learning Parsec and want to practice it by implementing function trim. Here is my code:
module Trim where
import Text.ParserCombinators.Parsec hiding(spaces)
trim = reverse.trimFront.reverse.trimFront
trimFront :: String->String
trimFront = readExpr trimParser
readExpr :: Parser String->String->String
readExpr parser input = case parse parser "trim" input of
Left err -> error $ show err
Right val -> val
spaces = many space
trimParser :: Parser String
trimParser = spaces >> many anyChar
my question is, How could I implement trim in function trimParser directly without having to implement trimFront first?
To trim the spaces from both sides of a string:
trim :: String -> String
trim = dropWhileEnd isSpace . dropWhile isSpace
Note that you may be better off using the following implementation of dropWhileEnd instead of the one from Data.List, depending on the situation:
dropWhileEnd :: (a -> Bool) -> [a] -> [a]
dropWhileEnd p = foldr
(\x xs -> if null xs && p x then [] else x : xs) []