Haskell: How to remove a non-number from a String - haskell

I have to write a code, that only returns numbers from a String.
I know how to remove a number from a String:
numbersInString = map removeNumbers.words
where
removeNumbers "" = ""
removeNumbers (s:ss)
| isNumber s = removeNumbers ss
| otherwise = s : removeNumbers ss
But I need it when deleting non-numbers.
For example:
removeNonNum :: String -> [String]
....
removeNonNum "234+8" == ["234", "8"]

words only splits on whitespace, so make your own recursive removeNonNum function (this code uses span :: (a -> Bool) -> [a] -> ([a], [a])):
import Data.Char
removeNonNum :: String -> [String]
removeNonNum "" = []
removeNonNum s#(x:_)
-- we have numbers at the start, extract them and add them to the list
| isNumber x =
let (numbers, rest) = span isNumber s
-- recurse onto the remaining parts
in numbers:removeNonNum rest
-- we don't have numbers at the start, skip them and continue recursing
| otherwise = removeNonNum $ dropWhile (not . isNumber) s

Related

Need help in subproblem of parser for polynomials (Haskell)

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
...

can i use a string as a whole string after cutting it to head and tails (a:as) and using recursion on it in Haskell?

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

Palindroms & Monads

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

how to replace a letter in string with Haskell

i have to make Haskell function called markDups that processes a string, replacing all repeated occurrences of a character with the underscore, "_", character.
here is my code i did so far.
makeBar :: Char -> [Char] -> [Char]
makeBar c (x:xs) | c == x = '_':makeBar c xs --turn into a "_"
| otherwise = x:makeBar c xs--ignore and move on
when I run this, here is my output with error message
output should be like this
what should I do?
This seems to work:
import Data.Set
main = putStrLn (markDups "hello world" empty)
markDups :: [Char] -> Set Char -> [Char]
markDups [] set = []
markDups (x:rest) set
| member x set = '_':(markDups rest set)
| otherwise = x:(markDups rest (insert x set))

How do I replace space characters in a string with "%20"?

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

Resources