Haskell replace space with Char - haskell

I'm trying to come up with a function that will replace all the blank spaces in a string with "%50" or similar and I know I'm messing up something with my types but can't seem to figure it out I have been trying the following (yes I have imported Data.Char)
newLine :: String -> String
newLine xs = if x `elem` " " then "%50"
I also tried the if then else statement but really didn't know what to do with the else so figured just lowercase all the letters with
newLine xs = [if x `elem` ' ' then '%50' else toLower x | x<-xs]
would like the else statement to simply do nothing but have searched and found no way of doing that so i figured if everything was lowercase it wouldn't really matter just trying to get this to work.

Try simple solution
newLine :: String -> String
newline "" = ""
newLine (' ':xs) = '%':'5':'0': newLine xs
newLine (x:xs) = x: newLine xs
or use library function

You're running into type mismatch issues. The approach you're currently using would work if you were replacing a Char with another Char. For example, to replace spaces with asterisks:
newLine xs = [if x == ' ' then '*' else toLower x | x<-xs]
Or if you wanted to replace both spaces and newlines with asterisks, you could use the elem function. But note that the elem function takes an array (or a String, which is the same as [Char]). In your example, you were trying to pass it a single element, ' '. This should work:
newLine xs = [if x `elem` " \n" then '*' else toLower x | x<-xs]
However, you want to replace a Char with a String ([Char]). So you need a different approach. The solution suggested by viorior looks good to me.

Well, the list comprehension is almost correct. Problem is:
"%50" is not a valid character literal, so you can't have '%50'. If you actually mean the three characters %, 5 and 0, it needs to be a String instead.
' ' is a correct character literal, but the character x can't be element of another char. You certainly mean simply x == ' '.
Now that would suggest the solution
[if x == ' ' then "%50" else toLower x | x<-xs]
but this doesn't quite work because you're mixing strings ("%50") and single-characters in the same list. That can easily be fixed though, by "promoting" x to a single-char string:
[if x == ' ' then "%50" else [toLower x] | x<-xs]
The result has then type [String], which can be "flattened" to a single string with the prelude concat function.
concat [if x == ' ' then "%50" else [toLower x] | x<-xs]
An alternative way of writing this is
concatMap (\x -> if x == ' ' then "%50" else [toLower x]) xs
or – exactly the same with more general infix operators
xs >>= \x -> if x == ' ' then "%50" else [toLower x]

To replace characters with possibly longer strings, one can follow this approach:
-- replace single characters
replace :: Char -> String
replace ' ' = "%50"
replace '+' = "Hello"
replace c | isAlpha c = someStringFunctionOf c
replace _ = "DEFAULT"
-- extend to strings
replaceString :: String -> String
replaceString s = concat (map replace s)
The last line can also be written as
replaceString s = concatMap replace s
or even
replaceString s = s >>= replace
or even
replaceString = (>>= replace)

import Data.List
newLine :: String -> String
newLine = intercalate "%50" . words

Related

Haskell how to remove excessive spaces in a String

Trying to create a function that removes all double/triple etc spaces in a
string (and merges them into one single space)
So far I have been able to get double spaces to remove, but not sure how to go about triple and more.
i.e "a b c d e" -> "a b c d e"
formatSpace :: String -> String
formatSpace [] = []
formatSpace (' ':' ':xs) = ' ': formatSpace xs
formatSpace (x:xs) = x: formatSpace xs
Thought about trying to turn all spaces into say '-' and then turn all those into a single space.
Been able to move and leading and trailing whitespace, but can't do this one
I'd do it like this:
formatSpace :: String -> String
formatSpace = foldr go ""
where
go x acc = x:if x == ' ' then dropWhile (' ' ==) acc else acc
This is maximally lazy and doesn't create any unnecessary intermediate data structures.

Converts a double space into single one anywhere in a String in Haskell

I've been trying to complete a function that converts
double space in a String into a single space in Haskell.
normaliseSpace:: String -> String
normaliseSpace (x:y:xs)= if x==' ' && y==' ' then y:xs
else xs
The problem with my code is that only converts double spaces in the beginning of a String. I suppose it has something to do with pattern matching, but as I am completely new to Haskell I really don't have an idea how to do it. Any help will be appreciated!
The reason this happens is because y:xs and xs will not recurse on te rest of the string. You thus want to perform the function on the rest of the string.
You thus should call normaliseSpace on xs as tail. For example:
normaliseSpace:: String -> String
normaliseSpace "" = ""
normaliseSpace (' ' : ' ' : xs) = ' ' : normaliseSpace xs
normalissSpace (x:xs) = x : normaliseSpace xs
Note that you need to add a pattern for the empty string (list) as well. Since otherwise eventually the recursion will reach the end of the list, and thus raise an error because there is no clause that can "fire".
If you want to reduce a sequence of spaces (two or more to one), then we even need to pass ' ' : xs through the normalizeSpace, like #leftroundabout says:
normaliseSpace:: String -> String
normaliseSpace "" = ""
normaliseSpace (' ' : ' ' : xs) = normaliseSpace (' ':xs)
normalissSpace (x:xs) = x : normaliseSpace xs
We can use an as-pattern here, like #JosephSible suggests:
normaliseSpace:: String -> String
normaliseSpace "" = ""
normaliseSpace (' ' : xs#(' ' : _)) = normaliseSpace xs
normalissSpace (x:xs) = x : normaliseSpace xs
May be you can simply do like
*Main> let normSpaces = unwords . words
*Main> normSpaces "hello world"
"hello world"

Insert space after every punctuation sign in a String Haskell

I have this function that checks if a character is one of these punctuation signs.
checkpunctuation:: Char -> Bool
checkpunctuationc = c `elem` ['.', ',', '?', '!', ':', ';', '(', ')']
I have to write another function that after every punctuation sign it adds a space
format :: String -> String
I know how to add space after a given number of characthers but don't know how to add after specific characters.
Simple recursive option:
format :: String -> String
format [] = []
format (x:xs) | checkpuntuationc x = x : ' ' : format xs
| otherwise = x : format xs
Another option is to use foldr with a helper function:
helper :: Char -> String -> String
helper x xs | checkpunctuation x = x : ' ' : xs
| otherwise = x : xs
The helper checks if the first character is a punctuation. If so it inserts a space, otherwise it does not.
and then define format as:
format :: String -> String
format = foldr helper []
A sample call:
*Main> format "Hello? Goodbye! You say goodbye!! (and I say Hello)"
"Hello? Goodbye! You say goodbye! ! ( and I say Hello) "
This function works also on "infinite strings":
*Main> take 50 $ format $ cycle "Hello?Goodbye!"
"Hello? Goodbye! Hello? Goodbye! Hello? Goodbye! He"
So although we feed it a string that keeps cycle-ing, and thus never ends, we can derive the first 50 characters of the result.
There's probably a more elegant way to do it, but
format :: String -> String
format s = concat [if (checkpunctuation c) then (c:" ") else [c] | c <- s]
will work (thanks, #Shou Ya!).
Edit based on comment
To count the total length of post-formatted punctuation characters, you can use
sumLength :: [String] -> Int
sumLength strings = 2 * (sum $ fmap length (fmap (filter checkpunctuation) strings))
as the it is twice the sum of the number of punctuation characters.

Haskell extract substring within a string

My goal is to find the number of times a substring exists within a string.
The substring I'm looking for will be of type "[n]", where n can be any variable.
My attempt involved splitting the string up using the words function,
then create a new list of strings if the 'head' of a string was '[' and
the 'last' of the same string was ']'
The problem I ran into was that I entered a String which when split using
the function words, created a String that looked like this "[2],"
Now, I still want this to count as an occurrence of the type "[n]"
An example would be I would want this String,
asdf[1]jkl[2]asdf[1]jkl
to return 3.
Here's the code I have:
-- String that will be tested on references function
txt :: String
txt = "[1] and [2] both feature characters who will do whatever it takes to " ++
"get to their goal, and in the end the thing they want the most ends " ++
"up destroying them. In case of [2], this is a whale..."
-- Function that will take a list of Strings and return a list that contains
-- any String of the type [n], where n is an variable
ref :: [String] -> [String]
ref [] = []
ref xs = [x | x <- xs, head x == '[', last x == ']']
-- Function takes a text with references in the format [n] and returns
-- the total number of references.
-- Example : ghci> references txt -- -> 3
references :: String -> Integer
references txt = len (ref (words txt))
If anyone can enlighten me on how to search for a substring within a string
or how to parse a string given a substring, that would be greatly appreciated.
I would just use a regular expression, and write it like this:
import Text.Regex.Posix
txt :: String
txt = "[1] and [2] both feature characters who will do whatever it takes to " ++
"get to their goal, and in the end the thing they want the most ends " ++
"up destroying them. In case of [2], this is a whale..."
-- references counts the number of references in the input string
references :: String -> Int
references str = str =~ "\\[[0-9]*\\]"
main = putStrLn $ show $ references txt -- outputs 3
regex is huge overkill for such a simple problem.
references = length . consume
consume [] = []
consume ('[':xs) = let (v,rest) = consume' xs in v:consume rest
consume (_ :xs) = consume xs
consume' [] = ([], [])
consume' (']':xs) = ([], xs)
consume' (x :xs) = let (v,rest) = consume' xs in (x:v, rest)
consume waits for a [ , then calls consume', which gathers everything until a ].
Here's a solution with
sepCap.
import Replace.Megaparsec
import Text.Megaparsec
import Text.Megaparsec.Char
import Data.Either
import Data.Maybe
txt = "[1] and [2] both feature characters who will do whatever it takes to " ++
"get to their goal, and in the end the thing they want the most ends " ++
"up destroying them. In case of [2], this is a whale..."
pattern = single '[' *> anySingle <* single ']' :: Parsec Void String Char
length $ rights $ fromJust $ parseMaybe (sepCap pattern) txt
3

Do some replacement in Haskell List Comprehensions

My questions is if I put in a string containing such as Hello, today is a Nice Day!! How could I get rid of spaces and punctuation and also replacing the uppercase letters with lowercase?
I know how to delete them but not how to replace them.
Also to get rid of the punctuation.
Sorry I don't know how to mess around with strings, only numbers.
testList xs = [if x = [,|.|?|!] then " " | x<-xs]
import Data.Char
If you want convert the punctuation to space and the characters from upper case to lower case:
testList xs = [if x `elem` ",.?!" then ' ' else toLower x | x<-xs]
Example: testList "TeST,LiST!" == "test list "
If you want to delete the punctuation and convert the characters from upper case to lower case:
testList2 xs = [toLower x | x<-xs, not (x `elem` ",.?!")]
Example: testList2 "Te..S,!t LiS?T" == "test list"
If you don't want or can not import Data.Char, this is an implementation of toLower:
toLower' :: Char -> Char
toLower' char
| isNotUppercase = char -- no change required
| otherwise = toEnum (codeChar + diffLowerUpperChar) -- char lowered
where
codeChar = fromEnum char -- each character has a numeric code
code_A = 65
code_Z = 90
code_a = 97
isNotUppercase = codeChar < code_A || codeChar > code_Z
diffLowerUpperChar = code_a - code_A
I've been without writing a code in Haskell for a long time, but the following should remove the invalid characters (replace them by a space) and also convert the characters from Uppercase to Lowercase:
import Data.Char
replace invalid xs = [if elem x invalid then ' ' else toLower x | x <- xs]
Another way of doing the same:
repl invalid [] = []
repl invalid (x:xs) | elem x invalid = ' ' : repl invalid xs
| otherwise = toLower x : repl invalid xs
You can call the replace (or repl) function like this:
replace ",.?!" "Hello, today is a Nice Day!!"
The above code will return:
"hello today is a nice day "
Edit: I'm using the toLower function from Data.Char in Haskell, but if you want to write it by yourself, check here on Stack Overflow. That question has been asked before.
You will find the functions you need in Data.Char:
import Data.Char
process str = [toLower c | c <- str , isAlpha c]
Though personally, I think the function compositional approach is clearer:
process = map toLower . filter isAlpha
To get rid of the punctuation you can use a filter like this one
[x | x<-[1..10], x `mod` 2 == 0]
The "if" you are using won't filter. Putting an if in the "map" part of a list comprehension will only seve to choose between two options but you can't filter them out there.
As for converting things to lowercase, its the same trick as you can already pull off in numbers:
[x*2 | x <- [1..10]]
Here's a version without importing modules, using fromEnum and toEnum to choose which characters to allow:
testList xs =
filter (\x -> elem (fromEnum x) ([97..122] ++ [32] ++ [48..57])) $ map toLower' xs
where toLower' x = if elem (fromEnum x) [65..90]
then toEnum (fromEnum x + 32)::Char
else x
OUTPUT:
*Main> testList "Hello, today is a Nice Day!!"
"hello today is a nice day"
For a module-less replace function, something like this might work:
myReplace toReplace xs = map myReplace' xs where
myReplace' x
| elem (fromEnum x) [65..90] = toEnum (fromEnum x + 32)::Char
| elem x toReplace = ' '
| otherwise = x
OUTPUT:
*Main> myReplace "!," "Hello, today is a Nice Day!! 123"
"hello today is a nice day 123"
Using Applicative Style
A textual quote from book "Learn You a Haskell for Great Good!":
Using the applicative style on lists is often a good replacement for
list comprehensions. In the second chapter, we wanted to see all the
possible products of [2,5,10] and [8,10,11], so we did this:
[ x*y | x <- [2,5,10], y <- [8,10,11]]
We're just drawing from two lists and applying a function between
every combination of elements. This can be done in the applicative
style as well:
(*) <$> [2,5,10] <*> [8,10,11]
This seems clearer to me, because it's easier to see that we're just
calling * between two non-deterministic computations. If we wanted all
possible products of those two lists that are more than 50, we'd just
do:
filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11]
-- [55,80,100,110]
Functors, Applicative Functors and Monoids

Resources