I wanted to write a Haskell function that takes a string, and replaces any space characters with the special code %20. For example:
sanitize "http://cs.edu/my homepage/I love spaces.html"
-- "http://cs.edu/my%20homepage/I%20love%20spaces.html"
I am thinking to use the concat function, so I can concatenates a list of lists into a plain list.
The higher-order function you are looking for is
concatMap :: (a -> [b]) -> [a] -> [b]
In your case, choosing a ~ Char, b ~ Char (and observing that String is just a type synonym for [Char]), we get
concatMap :: (Char -> String) -> String -> String
So once you write a function
escape :: Char -> String
escape ' ' = "%20"
escape c = [c]
you can lift that to work over strings by just writing
sanitize :: String -> String
sanitize = concatMap escape
Using a comprehension also works, as follows,
changer :: [Char] -> [Char]
changer xs = [ c | v <- xs , c <- if (v == ' ') then "%20" else [v] ]
changer :: [Char] -> [Char] -> [Char]
changer [] res = res
changer (x:xs) res = changer xs (res ++ (if x == ' ' then "%20" else [x]))
sanitize :: [Char] -> [Char]
sanitize xs = changer xs ""
main = print $ sanitize "http://cs.edu/my homepage/I love spaces.html"
-- "http://cs.edu/my%20homepage/I%20love%20spaces.html"
The purpose of sanitize function is to just invoke changer, which does the actual work. Now, changer recursively calls itself, till the current string is exhausted.
changer xs (res ++ (if x == ' ' then "%20" else [x]))
It takes the first character x and checks if it is equal to " ", if so gives %20, otherwise the actual character itself as a string, which we then concatenate with the accumulated string.
Note: This is may not be the optimal solution.
You can use intercalate function from Data.List module. It does an intersperse with given separator and list, then concats the result.
sanitize = intercalate "%20" . words
or using pattern matching :
sanitize [] = []
sanitize (x:xs) = go x xs
where go ' ' [] = "%20"
go y [] = [y]
go ' ' (x:xs) = '%':'2':'0': go x xs
go y (x:xs) = y: go x xs
Another expression of Shanth's pattern-matching approach:
sanitize = foldr go []
where
go ' ' r = '%':'2':'0':r
go c r = c:r
Related
I'm currently doing an assignment for college where we are implementing an polynomial calculator in Haskell.
The first part of the assignment is doing poly operations, and that is already done.
We get extra credit if we implement an parser for the polynomial, which I'm currently doing by turning a string to a tuple of [(factor, [(variable, exponent)])].
This means "-10y^4 - 5z^5" => "[(-10, [('y', 4)]), (-5, [('z', 5)].
The sub-problem I'm having trouble with is when I encounter polynomials like "5xy^2z^3" that should be stored as [(5, [('x',1), ('y', 2),('z',3)]], I don't know how to parse it.
Any suggestion on how I could approach this?
Thank you in advance for your help!
-- Slipts lists by chosen Char, only used with '+' in this project
split :: Char -> String -> [String]
split _ "" = []
split c s = firstWord : (split c rest)
where firstWord = takeWhile (/=c) s
rest = drop (length firstWord + 1) s
-- Remove all spaces from a string, for easier parsing
formatSpace :: String -> String
formatSpace = filter (not . isSpace)
-- Clever way to parse the polynomial, add an extra '+' before every '-'
-- so after we split the string by '+', it helps us keep the '-'
simplify_minus :: String -> String
simplify_minus [] = ""
simplify_minus (x:xs)
| x == '^' = x : head xs : simplify_minus (tail xs)
| x == '-' = "+-" ++ simplify_minus xs
| otherwise = x : simplify_minus xs
-- Splits an String by occurrences of '+' and creates a list of those sub-strings
remove_plus :: String -> [String]
remove_plus s = split '+' s
-- Removes multiplication on substrings
remove_mult :: [String] -> [[String]]
remove_mult [] = []
remove_mult (x:xs) = (remove_power (split '*' x)) : remove_mult xs
-- Function used to separate a variable that has an power. This translates ["y^2] to [["y", "2"]]
remove_power :: [String] -> [String]
remove_power [] = []
remove_power (x:xs) = (split '^' x) ++ remove_power xs
-- Wrapper function for all the functions necessary to the parser
parse_poly :: String -> [(Integer, String, Integer)]
parse_poly [] = []
parse_poly s = map (tuplify) (rem_m (remove_plus (simplify_minus (formatSpace s))))
rem_m :: [String] -> [String]
rem_m l = map (filter (not . (=='*'))) l
helper_int :: String -> Integer
helper_int s
| s == "" = 1
| s == "-" = -1
| otherwise = read s :: Integer
helper_char :: String -> String
helper_char s
| s == [] = " "
| otherwise = s
tuplify :: String -> (Integer, String, Integer)
tuplify l = (helper_int t1, helper_char t3, helper_int (drop 1 t4))
where (t1, t2) = (break (isAlpha) l)
(t3, t4) = (break (=='^') t2)
main :: IO()
main = do
putStr("\nRANDOM TESTING ON THE WAE\n")
putStr("--------------\n")
print(parse_poly "5*xyz^3 - 10*y^4 - 5*z^5 - x^2 - 5 - x")
-- [(5,"xyz",3),(-10,"y",4),(-5,"z",5),(-1,"x",2),(-5," ",1),(-1,"x",1)]
``
You have pretty much everything there already, but you do need to use break recursively to grab everything until the next variable. You probably should also use the similar span to first grab the coefficient.
parsePositiveMonomial :: String -> (Integer, [(Char, Integer)])
parsePositiveMonomial s = case span isDigit s of
([], varPows) -> (1, parseUnitMonomial varPows)
(coef, varPows) -> (read coef, parseUnitMonomial varPows)
where parseUnitMonomial [] = []
parseUnitMonomial (var:s') = case break isAlpha s' of
...
I am trying to use every character in the string in a function i have (that uses only one Char) but i am also trying to use that same string as a whole in the same recursive function to compare it to indvidual characters in another string (using elem). Is there a way i can use that string heads and tails and also the whole string, so that the string will not be cut after every recursion?
Code:
checkTrue :: TrueChar -> Char -> [Char] -> TruthValue
checkTrue a b c
| a == IsTrue b = AbsoluteTrue
| (a == IsFalse b) && (b `elem` c) = PartialTrue
| otherwise = NoneTrue
checkTruths :: [TrueChar] -> [Char] -> [TruthValue]
checkTruths [][] = []
checkTruths (a:as) (b:bs) = checkTrue a b (removeAbsoluteTrue (a:as) (b:bs)): checkTruths as bs
{- This is the line,
i wanted to use b as a string and also as b:bs. is this possible? -}
checkTruths _ _ = [NoneTrue]
You want an as-pattern, as documented in Section 3.17.1 of the Haskell 2010 report.
Patterns of the form var#pat are called as-patterns, and allow one to
use var as a name for the value being matched by pat. For example,
case e of { xs#(x:rest) -> if x==0 then rest else xs }
is equivalent
to:
let { xs = e } in
case xs of { (x:rest) -> if x==0 then rest else xs }
In your function, you'd write
checkTruths alla#(a:as) allb#(b:bs) = checkTrue a b (removeAbsoluteTrue alla allb): checkTruths as bs
I'm new to the Haskell. I am finding following task difficult:
Enter a string of characters. Output all palindromes to the file (use the IO monad to work with the file system and input / output, use the list monad to work with the strings).`
Any code is may be helpful. Thank you in advance!
This is what I have tried so far:
palindrome :: Char -> [String]
palindrome n
| n < 0 = []
| even n = map (\front -> front ++ reverse front) fronts
| odd n = map (\front -> front ++ tail (reverse front)) fronts
where ispalindrome :: (Integral a, Show a) => a -> Bool
ispalindrome x = show x = reverse (show x)
main = do
input <- getline
putStrLn print :: IO ()
So this is basically consists of 4 things.
Read Input from the stdin
Convert input string into list of strings
From the above list find out the strings which are palindromes
print these palindromes into file.
If you convert above into functions the signatures of these will be.
String -> [String]
[String] -> [String]
Don't bother about the signature of 1st and 4th for now. These are anyways one line code readily available on internet.
2 is a single function available in Data.List called words.
3 can be again in two parts. A function which find out if a given string is palindrome. Signature will be
String -> Bool
This is also one line code.
Once you have above function the only part remaining is filtering out the strings which are palindromes in given list of strings.
isPalindrome
My haskell is a bit rusty so I don't promise the code below will work %100 yet I tried to stick to the main idea.I hope this answer helps. If you think anything is wrong both logically and syntactically, just write a comment and I will fix it asap.
isPalindrome :: [Char] -> Boolean
isPalindrome w = isPalindrome' w reverse w
where
isPalindrome' :: [Char] -> [Char] -> Boolean
isPalindrome' [] [] = true
isPalindrome' (x:xs) (y:ys) = if x == y then isPalindrome' xs ys else false
isPalindrome' _ _ = false
function above should be fine for checking for palindromes.
for writing to file part, you can create a list of palindromes first, then write all palindromes to a file in another function. so basically, first you split your string into words, then for words in given string you find palindromes, then you write the palindromes into a file.
how to read string from user?
main = do
userInput <- getLine
how to split word with delimiter?
split :: Char -> [Char] -> [[Char]]
split delimiter string = split' delimiter string [] []
where
split' :: Char -> [Char] -> [Char] -> [[Char]] -> [[Char]]
split' delim [] substr splittedStr = splittedStr if substr == [] else reverse substr ++ splittedStr
split' delim (x:xs) substr splittedStr = if x == delim then split' delim xs [] (reverse substr) ++ splittedSubstr else split' delim xs (x ++ substr) splittedSubstr
main idea is you stack characters until you see your delimeter and store them in a list when you see a delimiter.
how to filter palindromes in list?
to filter palindromes in list you use haskell's filter function as
filter isPalindrome (split ' ' userInput)
In the end, you can write a main block to run all of this in right order
main = do
userInput <- getLine
let splittedInput = split ' ' userInput
let palindromes = filter isPalindrome splittedInput
let output = concat (intersperse "\n" palindromes)
writeFile "file.txt" output
This is an extension of this question: Haskell replace characters in string
I would like to tweak the following expression to replace a char with a string
let replaceO = map (\c -> if c=='O' then 'X'; else c)
In the end, I would the following results (XX can be a string of any length):
replaceO "HELLO WORLD"
"HELLXX WXXRLD"
You can use concatMap:
let replace0 = concatMap (\c -> if c=='O' then "X" else "XX")
You kind formulate your problem in terms of traversing and accumulating based on a condition, something like this,
replace :: String -> Char -> String -> String
replace xs c s = foldr go [] xs
where go x acc = if x == c then acc ++ s
else acc ++ [x]
For you example:
>replace "HELLO WORLD" 'O' "XXX"
> "HELLXXX WXXXRLD"
Supposing I had the string "HELLO WORLD" is there a way I can call a function that replaces the character 'O' in the string with the character 'X' so that the new string would look like "HELLX WXRLD"?
How about:
let
repl 'o' = 'x'
repl c = c
in map repl "Hello World"
If you need to replace additional characters later, just add clauses to the repl function.
Sorry for picking up this old thread but why not use lambda expressions?
λ> let replaceO = map (\c -> if c=='O' then 'X'; else c)
λ> replaceO "HELLO WORLD"
"HELLX WXRLD"`
Alternative 1 - Using MissingH
First:
import Data.List.Utils (replace)
Then use:
replace "O" "X" "HELLO WORLD"
Alternative 2 - Using Control.Monad
One funny bastard:
import Control.Monad (mfilter)
replace a b = map $ maybe b id . mfilter (/= a) . Just
Example:
λ> replace 'O' 'X' "HELLO WORLD"
"HELLX WXRLD"
Alternative 3 - Using if
Amon's suggestions was probably the finest I believe! No imports and easy to read and understand!
But to be picky - there's no need for semicolon:
replace :: Eq a => a -> a -> [a] -> [a]
replace a b = map $ \c -> if c == a then b else c
If you depend on the text package (like 99.99% of Haskell applications), you can use T.replace:
>>> replace "ofo" "bar" "ofofo"
"barfo"
Here's another possible solution using divide and conquer:
replaceO [] = []
replaceO (x:xs) =
if x == 'O'
then 'X' : replaceO xs
else x : replaceO xs
First, you set the edge condition "replaceO [] = []".
If the list is empty, there is nothing to replace, returning an empty list.
Next, we take the string and divide it into head and tail. in this case 'H':"ELLOWORLD"
If the head is equal to 'O', it will replace it with 'X'. and apply the replaceO function to the rest of the string.
If the head is not equal to 'O', then it will put the head back where it is and apply the replaceO function to the rest of the string.
replace :: Char -> Char -> String -> String
replace _ _ [] = []
replace a b (x : xs)
| x == a = [b] ++ replace a b xs
| otherwise = [x] ++ replace a b xs
I'm new to Haskell and I've tried to make it simpler for others like me.
I guess this could be useful.
main = print $ charRemap "Hello WOrld" ['O','o'] ['X','x']
charRemap :: [Char] -> [Char] -> [Char] -> [Char]
charRemap [] _ _ = []
charRemap (w:word) mapFrom mapTo =
if snd state
then mapTo !! fst state : charRemap word mapFrom mapTo
else w : charRemap word mapFrom mapTo
where
state = hasChar w mapFrom 0
hasChar :: Char -> [Char] -> Int -> (Int,Bool)
hasChar _ [] _ = (0,False)
hasChar c (x:xs) i | c == x = (i,True)
| otherwise = hasChar c xs (i+1)