I have found nothing about how to strip a string (remove leading/trailing characters) in Haskell, and there’s no place place to find such a strip or chomp function (correct me if I’m wrong).
What am I gonna do?
Have a look at Data.Text. Anything that uses Prelude lists, such as Strings, usually performs poorly, especially with functions like stripR. Some consider it a mistake from the past, because it has infected a lot of (otherwise sensible) interfaces with the inefficiencies of using singly linked lists of characters (String) for textual data.
The functions you're looking for are, in order: dropWhile, dropWhileEnd, dropAround, stripStart, stripEnd, strip.
Note that there's no specific function for stripping based on character equality. You don't really gain anything from aliasing dropX with a predicate, unless it's a very commonly used one like Data.Char.isSpace.
First off, you should use Text (from the text package) instead of String, since text is much more efficient.
Also, text already has this function:
-- Remove leading and trailing white space from a string.
strip :: Text -> Text
The more general approach would be to pass a predicate to the strip functions, so one could stripL isSpace e.g. to remove all leading white space.
Then stripL would however just be an alias for dropWhile.
For the stripping of the end, a potentially more efficient version uses foldr,
stripR :: (a -> Bool) -> [a] -> [a]
stripR pred = foldr keepOrDrop []
where
keepOrDrop c xs
| pred c = case xs of
[] -> []
_ -> c:xs
| otherwise = c:xs
that can start producing output without traversing the entire input list, and is efficient if there are no long runs of elements satisfying the predicate it the input.
Here are 3 functions and 3 currified aliased functions to make it through:
stripL :: Char -> String -> String
stripL x = dropWhile (==x)
stripR :: Char -> String -> String
stripR x = reverse . stripL . reverse
strip :: Char -> String -> String
strip x = stripL x . stripR x
chompL :: String -> String
chompL = stripL ' '
chompR :: String -> String
chompR = stripR ' '
chomp :: String -> String
chomp = strip ' '
What do you think? Is it possible to add such functions to Data.String?
Related
For example I have the String "Hello what a nice day" it is supposed to convert to this: "Hello What A Nice Day".
I want to convert it with a map.
I have tried:
upperFirst list = map (\a -> toUpper a) . filter(\(x:sx:xs) -> isSpace x && isLower sx) list
This way I wanna check, if my current char is " " and the char behind it is in lower case.
But obviously it doesn't work.
Maybe someone can help me and explain it for me.
You can work with words :: String -> [String] and unwords :: [String] -> String to split a string into a list of strings and transfer it back. Then you only need to convert each individual word, so:
upperFirst :: String -> String
upperFirst = unwords. … . words
where I leave the …-part as an exercise.
I'm trying to write a code in which would take a input string say:
I love bacon, but I love bananas more.
and return ["bacon","bananas"] as output.
However I've ran into some troubles with my code, as I can't seem to properly implement this, currently my idea is that I would input a string and then use word() to split up the string into a string list, and then call getWrods to extract all the words with "ba" as their prefix and then return a list composed of words that start with "ba" for the main function allWords.
My code is as follows:
getWords:: String -> [String] -> [String]
getWords n [] = []
getWords n (x:xs)
| n isPrefixOf x = [x] ++ getWords n xs
|otherwise = getWords n xs
allWordss:: String -> [String]
allWordss n = getWords("ba" words(n))
I think that by using filter :: (a -> Bool) -> [a] -> [a] here, you make the problem easier.
You can as filter condition use - like in your code isPrefixOf :: Eq a => [a] -> [a] -> Bool, but you here wrote in in "infix" notation, but without writing backticks. You thus can call the function with:
isPrefixOf n x
or:
n `isPrefixOf` x
A final problem with your code is that you write:
getWords("ba" words(n))
Here you seem to call a function with brackets, which is quite common in languages like Java, C++, etc. In Haskell however, a function f is called with a parameter x like f x, so you make a call with:
getWords "ba" (words n)
If we use filter here, we thus obtain:
allBaWords :: String -> [String]
allBaWords n = filter (\x -> isPrefixOf "ba" x) (words n)
or shorter:
allBaWords :: String -> [String]
allBaWords = filter (isPrefixOf "ba") . words
We can break up the problem into three logical parts:
Separate a string into a list of words.
Recognize whether a word starts with "ba".
Given a list, get a list of all the elements that satisfy a certain condition (often called a predicate).
Let's start by importing a couple standard modules:
import Data.List (isPrefixOf)
import Data.String (words)
Let's start with (2):
startsWithBa :: String -> Bool
startsWithBa s = -- something using isPrefixOf
As others have noted, you have to enclose isPrefixOf in backticks if you want to use it infix (which most people tend to do so it reads nicely).
Now to separate the string into words, we use
words :: String -> [String]
To extract just the strings that start with "ba", we can use the function
filter :: (a -> Bool) -> [a] -> [a]
I'll let you try to put these pieces together.
I'm trying to write a Haskell function that takes a string of pairs of letters, and exchanges the letters of the pair in a string of all letters, but what I've come up with feels awkward and unidiomatic.
I have
swap a b = map (\x-> if x == a then b else if x == b then a else x)
sub n = foldr (.) id (zipWith swap (head <$> splitOn "." n) (last <$> splitOn "." n)) ['A'..'Z']
which works well enough giving
> sub "RB.XD.EU.ZM.IJ"
"ARCXUFGHJIKLZNOPQBSTEVWDYM"
and
> sub "YC.LU.EB.TZ.RB.XD.IJ"
"ARYXBFGHJIKUMNOPQESZLVWDCT"
but I'm new to Haskell and feel like my approach — especially my swap helper function (which I only use here) — is more elaborate than it needs to be.
Is there a better, more idiomatic, approach to this problem; especially one that takes advantage of a language feature, builtin, or library function that I've missed?
Doing a left fold over the substitution list makes the code shorter:
import Data.List
import Data.List.Split
sub = foldl' swap ['A'..'Z'] . splitOn "." . reverse
where
swap az [a,b] = map (\x -> if x == a then b else if x == b then a else x) az
Drop the reverse if you don't care whether EB or RB is swapped first.
If you'd want to replace instead of a swap:
import Data.List
import Data.List.Split
replace needle replacement haystack =
intercalate replacement (splitOn needle haystack)
rep = foldl' replace' ['A'..'Z'] . splitOn "."
where
replace' az [a,b] = replace [a] [b] az
I'd break the problem down a bit more. It's important to remember that shorter code is not necessarily the best code. Your implementation works, but it's too compact for me to quickly understand. I'd recommend something more like
import Data.Maybe (mapMaybe)
swap = undefined -- Your current implementation is fine,
-- although you could rewrite it using
-- a local function instead of a lambda
-- |Parses the swap specification string into a list of
-- of characters to swap as tuples
parseSwap :: String -> [(Char, Char)]
parseSwap = mapMaybe toTuple . splitOn "."
where
toTuple (first:second:_) = Just (first, second)
toTuple _ = Nothing
-- |Takes a list of characters to swap and applies it
-- to a target string
sub :: [(Char, Char)] -> String -> String
sub charsToSwap = foldr (.) id (map (uncurry swap) charsToSwap)
The equivalent to your sub function would be
sub swapSpec = foldr (.) id (map (uncurry swap) $ parseSwap swapSpec)
But the former is probably easier to understand for most haskellers. You could also do more transformations more easily to your swap specification as a list of tuples making it more powerful overall. You essentially decouple the representation of the swap specification and the actual swapping. Even for small programs like this it's important to maintain loose coupling so that you develop a habit for when you write larger programs!
This implementation also avoids recalculating splitOn for the swap specification string.
(I wasn't able to execute this code because I'm on a computer without Haskell installed, if anyone notices any bugs please edit to fix.) Tried it out in FPComplete, output matches #raxacoricofallapatorius'.
Some things I noticed from reading your code (I haven't tried to rewrite it). My first suggestion involves separation of concerns:
I'm trying to write a Haskell function that takes a string of pairs of letters, and exchanges the letters of the pair in a string of all letters
That means the a more natural type for your function would be:
sub :: [(Char, Char)] -> String -> String
Or, using Data.Map for more efficient lookups:
sub :: Map Char Char -> String -> String
Which is a lot more precise than taking a string with dot-separated pairs. You can then generate the associations between Chars in a separate step:
parseCharPairs :: String -> Map Char Char
Ideally you should also handle invalid inputs (e.g. AB.CDE) and empty input strings.
my swap helper function (which I only use here)
Then you probably should define it in a where clause. I would also avoid the name swap, as there is a relatively common function in Data.Tuple with the same name (swapLetters might be a nice choice).
sub n = foldr (.) id -- etc.
foldr (.) id (fmap f xs) y is the same thing as foldr f y xs. I'm almost certain this can be rewritten in a simpler way.
Because of the limited amount of resources, I need to propose a question here. I have been struggling with functional programming, the endless Haskell tutorials don't really help me. So what I want to achieve, in Clean language, is to split a string like " car cow cat " to a list of strings ["car","cow","cat"]. Can you provide me a detailed answer (does not have to be complete code), on how to iterate through this string, and especially the part when the newly constructed strings are added to the list?
I'm going to offer a simple solution. There are infinitely better ways of doing this in Haskell, but it's the simplest I can think for someone new in functional programming, without using any specifically Haskell function like takeWhile, or even any folds and maps...
You basically want to simulate iterating over a list, so here is what I suggest:
Define a function that will take a string and a split-by character. This function will return a list of strings - spliton :: String -> Char -> [String]
To move over the list, we'll want to gobble up characters until we hit one of our splitting characters. We'll also want to save the word we've saved up until now, and the entire list of words.
For that, we'll define a subfunction that will save the states
spliton' :: String -> Char -> String -> [String] -> [String]
spliton' [] _ sofar res = res ++ [sofar]
I've also included the simplest clause - an empty string. When our string is empty, we'll just want to return what we have saved so far.
Now lets move on to our actual recursive function:
If we hit our split character, we'll add the string we have saved so far to the list and restart with an empty current-state string
If we don't hit the split character, we'll add the character to the current-state string
spliton' (currchar:rest) splitby sofar res
| currchar==splitby = spliton' rest splitby "" (res++[sofar])
| otherwise = spliton' rest splitby (sofar++[currchar]) res
So, to summarize our code:
spliton :: String -> Char -> [String]
spliton source splitchar = spliton' source splitchar [] []
spliton' :: String -> Char -> String -> [String] -> [String]
spliton' [] _ sofar res = res ++ [sofar]
spliton' (currchar:rest) splitby sofar res
| currchar==splitby = spliton' rest splitby "" (res++[sofar])
| otherwise = spliton' rest splitby (sofar++[currchar]) res
Note: This will not however get rid of the empty string - meaning if you have many superfluous spaces - you'll get them added to the list. I'll leave you to think how to handle that case - hope this can help you get started.
Let's split this up in several sub-problems:
Make a list of characters from the string so that we can easily apply pattern matching.
Scrape the initial part of the list (as long as possible with only spaces or only not-spaces), and only keep it when it is not whitespace.
Repeat the second step while the list is non-empty.
The first thing can be done using fromString. For the second and third step, we define a helper function:
scrape :: [Char] -> [String]
scrape [] = []
scrape cs=:[c:_]
| isSpace c = scrape (dropWhile isSpace cs)
| otherwise = [toString word:scrape rest]
where
(word,rest) = span (not o isSpace) cs
The first alternative is the base case to match the empty list. The second alternative matches the whole list cs with a first element c. If the first character is a space, we recursively (step 3) call the same function on the same list without the initial part of spaces. If the first character is not a space, we use span :: (a -> Bool) [a] -> ([a], [a]) to split the list in the initial part that is a word, and the rest. We store the word using toString as a string, and recursively call scrape for the rest of the list.
Now, we only need a wrapper to make this a function with the type String -> [String]:
split :: String -> [String]
split s = scrape (fromString s)
where
scrape :: [Char] -> [String]
scrape [] = []
scrape cs=:[c:_]
| isSpace c = scrape (dropWhile isSpace cs)
| otherwise = [toString word:scrape rest]
where
(word,rest) = span (not o isSpace) cs
Note that you can easily abstract from the delimiter, by passing a character d and replacing isSpace c with c == d and (not o isSpace) by ((<>) d). Alternatively, you can choose to not pass a character d but a function isDelim :: Char -> Bool. You then get isDelim c and (not o isDelim), respectively.
I've started to learn haskell for real recently, and I'm doing some exercises from wikibooks.
I'm doing exercise with RLE encoding, and I've come with solution like this:
import Data.List
rle :: String -> [(Int,Char)]
rle [] = []
rle xs = zip lengths chars
where
groups = group xs
lengths = map length groups
chars = map head groups
rle_toString :: [(Int, Char)] -> String
rle_toString [] = []
rle_toString (x:xs) = show (fst x ) ++ show (snd x) ++ rle_toString xs`
Not a very elegant solution, but it almost works. The problem is, that I get output like this: "7'a'8'b'7'j'6'q'3'i'7'q'1'p'1'a'16'z'2'n'". The single quotes with chars are not vetry elegant. How can I achieve output like: "7a8b7j6q3i7q1p1a16z2n"?
show is used to print values as they appear in Haskell source code, and thus puts single quotes around characters (and double quotes around strings, and so on). Use [snd x] instead to show just the character.
In Haskell, String is just shorthand for List of Char [Char]. For example, the String "Foo" can also be written like this: ['F','o','o']. So, to convert a single character to a string, just put in in brackets: [char].
The problem is your use of show on a character. show 'a' == "'a'".
The solution is to realize that strings are just lists of characters, so if c is a character, then the one-character string that contains c is just [c].