I am trying to delete every space between words and uppercase every letter of a word with foldr.
I have tried it with map:
deleteSpaces:: String -> String
deleteSpaces word = (filter(not . isSpace) .unwords . map (\(x:xs) -> (toUpper x):xs) . words) word
it works.
But with foldr I always get an error:
deleteSpacesF :: String -> String
deleteSpacesF word = (filter(not . isSpace) . unwords . foldr (\(x:xs) acc -> (toUpper x) : xs : acc) [] . words) word
I have also tried (\x acc -> (toUpper (head x)):(tail x):acc)
The error is:
• Couldn't match type ‘Char’ with ‘[Char]’
Expected: String
Actual: Char
• In the first argument of ‘(:)’, namely ‘(toUpper x)’
In the expression: (toUpper x) : xs : acc
In the first argument of ‘foldr’, namely
‘(\ (x : xs) acc -> (toUpper x) : xs : acc)’
x is a Char, not a String, so head x and tail x, make no sense. You use x as the head and xs as the tail, so.
Here it thus looks like:
deleteSpacesF :: String -> String
deleteSpacesF = concat . foldr (\(x:xs) -> ((toUpper x : xs) :)) [] . words
you can also omit the concat with:
deleteSpacesF :: String -> String
deleteSpacesF = foldr (\(x:xs) -> (toUpper x :) . (xs ++)) [] . words
You could do something like this:
process = concat . map cap . words
where
cap [] = []
cap (x:xs) = (toUpper x):xs
This defines a helper function to capitalise the first letter, which can then be mapped over each word derived from splitting the original string.
Note that words will also treat tabs and newlines as separators along with regular spaces, so you may need to modify that if you only care about space characters.
Related
In the following exercise i want to manipulate a random string input by using functions.
Step 1: I want to remove all characters which are not digits, letters or spaces
Step 2: I want to replace all spaces with '_'
Step 3: I want to convert all numbers to spaces
Step 4: I want to replace all 'a' with 'z' and all 'A' with 'Z'
For lists i already used the filter function and i am wondering if this function can also be used for string inputs. I am not quite sure how to approach this exercise.
Update: I found an approach to solve step 1 and step 3 but i am not quite sure how to put the different functions together in a function which includes every step. Is it possible to call the different functions one after another in the right order in some kind of main function?
import Data.Char
toUpperStr xs = map toUpper xs -- function to convert lower to upper
dropInvalids xs = (filter (\x -> isUpper x || isSpace x || isDigit x)) $
toUpperStr xs
replaceBlank [] = [] -- function to replace " " with "_"
replaceBlank (x:xs) =
if x == ' '
then '_' : replaceBlank xs
else x : replaceBlank xs
Yes, absolutely! That's one of the beautiful things about Haskell.
You can treat Strings as [Char]. In fact, that's what they are!
In GHCi, type :i String - you get type String = [Char].
You can easily compose functions. There's an operator for that, (.).
So (f . g) x is f (g x).
I would improve the code in a few key ways.
Firstly, make the replaceBlank function more general, so it takes a condition and a replacement function.
Secondly, compose all the functions in a "main" function, as you call it.
But do not name the main function main! That name is reserved for the IO action of a program.
It's also important not to think of the final function as "calling" the other functions.
That is imperative terminology, here, we are applying the function(s).
Also, why does your dropInvalids contain a toUpperStr? You never specified the string to be all uppercase in the end.
Also also, be sure to declare the type of your functions.
In this case, the following would be the correct code:
import Data.Char
dropInvalids :: [Char] -> [Char]
dropInvalids = filter (\x -> isLetter x || isSpace x || isDigit x)
-- isLetter exists
replace' :: (a -> Bool) -> (a -> a) -> [a] -> [a]
replace' _ _ [] = []
replace' f g (x:xs) =
if f x
then g x : replace' f g xs
else x : replace' f g xs
-- To replace one value with another, use replace (== a) (const b).
replaceWith :: (a -> Bool) -> a -> [a] -> [a]
replaceWith f b = replace' f (const b)
replace :: Eq a => a -> a -> [a] -> [a]
replace a b = replace' (== a) (const b)
-- The Eq makes sure you can check for equality.
manipulateString :: [Char] -> [Char]
manipulateString = replace 'A' 'Z' . replace 'a' 'z' . replaceWith isDigit ' ' . replace ' ' '_' . dropInvalids
I'm trying to write a function of the form
f :: String -> [String]
f str = ...
that returns the list of all the strings formed by removing exactly one character from str. For example:
ghci> f "stack"
["tack","sack","stck","stak","stac"]
Because String and [Char] are synonymous, I could use the index, but I know that you should avoid doing that in Haskell. Is there a better way besides using the index?
You could use recursion like so:
f :: [a] -> [[a]]
f [] = []
f (s:ss) = ss : map (s:) (f ss)
The Josh Kirklin's solution as a one-liner:
f = tail . foldr (\x ~(r:rs) -> (x : r) : r : map (x :) rs) [[]]
Maybe a more readable way to describe it is:
gaps :: [a] -> [[a]]
gaps xs = zipWith removeAt [0..] $ replicate (length xs) xs
removeAt i xs = ys ++ zs
where
(ys,_:zs) = splitAt i xs
But practically, it is slower than the other solutions.
I was solving 10th problem from 99 problems in Haskell.
My solution was
-- Problem 10
encode:: String -> [(Int, Char)]
encode [] = []
encode (x:xs) = (length $ x : takeWhile (==x) xs, x) : encode $ dropWhile (==x) xs
The error that I got was
Prelude> :l 10-20.hs
[1 of 1] Compiling Main ( 10-20.hs, interpreted )
10-20.hs:4:17:
Couldn't match expected type `[Char] -> [(Int, Char)]'
with actual type `[(Int, Char)]'
The first argument of ($) takes one argument,
but its type `[(Int, Char)]' has none
In the expression:
(length $ x : takeWhile (== x) xs, x) : encode
$ dropWhile (== x) xs
In an equation for `encode':
encode (x : xs)
= (length $ x : takeWhile (== x) xs, x) : encode
$ dropWhile (== x) xs
10-20.hs:4:56:
Couldn't match expected type `[(Int, Char)]'
with actual type `String -> [(Int, Char)]'
In the second argument of `(:)', namely `encode'
In the expression: (length $ x : takeWhile (== x) xs, x) : encode
In the expression:
(length $ x : takeWhile (== x) xs, x) : encode
$ dropWhile (== x) xs
Failed, modules loaded: none.
Once I changed the code to
-- Problem 10
encode:: String -> [(Int, Char)]
encode [] = []
encode (x:xs) = (length $ x : takeWhile (==x) xs, x) : encode (dropWhile (==x) xs)
It compiles and run fine. Notice I just changed encode $ dropWhile (==x) xs to encode (dropWhile (==x) xs)
I have two questions here
How do I infer the change that I did (removing the $ application) from the GHCi error shown? I am unable to decipher this from the error thrown
Why does removing the $ work here?
($) performs function application with very low precedence, so
(length $ x : takeWhile (==x) xs, x) : encode $ dropWhile (==x) xs
is equivalent to
((length $ x : takeWhile (==x) xs, x) : encode) (dropWhile (==x) xs)
Which is ill-typed for two reasons. Firstly,
((length $ x : takeWhile (==x) xs, x) : encode)
would be a list built with the outermost (:), and not a function, and so it cannot be applied (thus the first error about the "first argument of ($)" which should take one argument). Secondly, encode is a String -> [(Int, Char)] function, and so it can't be the second argument of the outermost (:) (which should be a [(Int, Char)] given the type of the first argument, thus the second error).
I want to make a first character of a string if it's not. This is what I'm doing:
import Data.Char
onlyCapitals :: [String] -> [String]
onlyCapitals [] = []
onlyCapitals (x:xs) = if isUpper $ head x
then x ++ onlyCapitals xs -- 1
else toUpper (head x) : tail x ++ onlyCapitals xs -- 2 and 3
main = print $ onlyCapitals ["Aaaa", "bbb", "ffff"]
And I got 3 errors:
Couldn't match type `Char' with `[Char]'
Expected type: [String]
Actual type: String
Couldn't match type `Char' with `[Char]'
Expected type: String
Actual type: Char
Couldn't match type `Char' with `[Char]'
Expected type: [String]
Actual type: String
The first thing to realize
(++) :: [a] -> [a] -> [a]
(:) :: a -> [a] -> [a]
So your first error is that you try to do something like String ++ [String] which is a type error, instead you want (:)
The next problem is
toUpper (head x) : tail x ++ onlyCapitals xs
The problem is the associativity and precedence of ++ and : are both the same level to the right. So this is parsed as
toUpper (head x) : (tail x ++ onlyCapitals xs)
this is fixed with explicit parens and switching ++ to : again
(toUpper (head x) : tail x) : onlyCapitals xs
Style notes
Now this works, except if you pass it an empty string in which case it'll crash. Instead maybe something like this would be nicer
onlyCapitals :: [String] -> [String]
onlyCapitals = map cap
where cap "" = ""
cap (x : xs) = toUpper x : xs
We abstract away the explicit recursion and construction of the list and just leave it to map. Then we properly handle "" and capitalize the first character of nonempty strings.
Let's take the line you marked -- 1: then x ++ onlyCapitals xs. The type of x is String, while the type of onlyCapitals xs is [String]. The ++ operator requires its two operands to be of the same type and it assumes them to be lists. The left operand is a [Char] and it then expect the right operand to be [Char] as well. As the right operand is in fact [String], which is [[Char]], it thus reports: "couldn't match type [Char] with [[Char]]", simplified to "couldn't match type Char with [Char]", as the 'outer' list could be matched.
So you don't want to use the ++ operator there, but rather the : operator. The other errors stem from a similar problem on the line marked -- 2 and 3 and the solution is to very carefully check what types your subexpressions have and in what order the operators are applied.
One note on this method: you don't actually need the conditional. toUpper on something that is already a capital works just fine, so the 'else' can just be applied to every member of the list.
So I write a function with the definition
getLastDigits :: String -> String
which finds repeating digits on the end of a String
So, for example.
getLastDigits "1000" should give "000"
getLastDigits "19990299" should give "99"
Coming from a java background I'm not quite sure how to structure this program. I'm thinking of using foldr but I'm fairly sure I can't stop the fold half way when the repeating digits end.
-edit solved. Use the group function.
Okay then, if it is not homework:
lastDigits :: String -> String
lastDigits s = firstDigits . reverse $ s
where firstDigits :: String -> String
firstDigits (x:xs) = x : takeWhile (== x) xs
firstDigits [] = []
import Data.Char (isDigit)
getLastTheSame :: Eq a => (a -> Bool) -> [a] -> [a]
getLastTheSame pred xs = f (reverse xs)
where f (y : ys) | pred y = y : takeWhile (== y) ys
f _ = []
lastDigits :: String -> String
lastDigits = getLastTheSame isDigit
You say you want repeating digits from the end of the string. I presume that if the last character is not a digit then you want the empty string returned.
Recall that type String = [Char].