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.
Related
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.
I am new Haskell learner, and trying to do count words, but there are errors. how can i change to code and show the result like this
countWords ["friend","she","she"]
>[("friend",1),("she",2)
here is code
Prelude Data.List> countWords xs = map(\w -> (head w, length w))
$group $ sort $ words xs
Prelude Data.List> countWords ["hello", "hello", "world"]
:101:13: error:
• Couldn't match expected type ‘Char’ with actual type ‘[Char]’
• In the expression: "hello"
In the first argument of ‘countWords’, namely
‘["hello", "hello", "world"]’
In the expression: countWords ["hello", "hello", "world"]
:101:22: error:
• Couldn't match expected type ‘Char’ with actual type ‘[Char]’
• In the expression: "hello"
In the first argument of ‘countWords’, namely
‘["hello", "hello", "world"]’
In the expression: countWords ["hello", "hello", "world"]
:101:31: error:
• Couldn't match expected type ‘Char’ with actual type ‘[Char]’
• In the expression: "world"
In the first argument of ‘countWords’, namely
‘["hello", "hello", "world"]’
In the expression: countWords ["hello", "hello", "world"]
Thank you
As #chi said - words :: String -> [String] so either change the input type for your function to a single string of words separated by whitespace, or omit the words part, i.e.
countWords :: [String] -> [(String,Int)]
countWords xs = map (\w -> (head w, length w)) $ group $ sort xs
an example usage for this:
Prelude Data.List> countWords ["hello", "hello", "world"]
> [("hello",2),("world",1)]
or
countWords :: String -> [(String,Int)]
countWords xs = map (\w -> (head w, length w)) $ group $ sort $ words xs
an example usage:
Prelude Data.List> countWords "hello hello world"
> [("hello",2),("world",1)]
Let's break this down to a simpler example:
Prelude> xs = ["hello", "hello", "world"]
Prelude> words xs
<interactive>:2:7: error:
• Couldn't match type ‘[Char]’ with ‘Char’
Expected type: String
Actual type: [[Char]]
• In the first argument of ‘words’, namely ‘xs’
In the expression: words xs
In an equation for ‘it’: it = words xs
As you can see, we get a type error at the application of words. Further investigation shows us the problem:
Prelude> :t words
words :: String -> [String]
Prelude> :t xs
xs :: [[Char]]
Here we see the types for words and xs. First, words expects a String as its argument. However, xs type is [[Char]]. Since [Char] is the same as String, xs type can also be given as [String].
Now we see the problem. You are passing a [String] (a list of Strings) to a function which expects a single String.
I was trying to rewrite the Data.List.group using foldr, here is my code:
group' :: (Eq a) => [a] -> [[a]]
group' xs = foldr pack' [[head xs]] (tail xs)
where
pack' x acc = if x == (head $ head acc)
then x:(head acc)
else [x]:acc
now I get these errors:
Occurs check: cannot construct the infinite type: t0 = [t0]
In the expression: x
In the first argument of `(:)', namely `[x]'
In the expression: [x] : acc
And:
Occurs check: cannot construct the infinite type: t0 = [t0]
Expected type: [[t0]]
Actual type: [[[t0]]]
In the second argument of `(:)', namely `acc'
In the expression: [x] : acc
In the expression:
if x == (head $ head acc) then x : (head acc) else [x] : acc
Both refer to the last line.
I'm pretty sure I'm missing something but I don't really catch what since the type of pack' should be:
pack` a -> [[a]] -> [[a]]
This line:
then x:(head acc)
And this one
else [x]:acc
Should have the same type. But each are consing different types to the head of a list. This is what it means when it says "cannot construct the infinite type: t0 = [t0]". If a = [a] then it must be an infinite-dimensional list.
This is really a comment, and it was already brought forth by chi, but perhaps it help to actually show this: the "correct" way to do such a function is without head and tail, which can be replaced with pattern matching. Also, if is typically nicer rewritten as guards.
group' :: (Eq a) => [a] -> [[a]]
group' (x0:xi) = foldr pack' [[x0]] xi
where
pack' xn acc#((x:xs):xss)
| xn == x = (xn : x : xs) : xss
| otherwise = [x]:acc
With warnings enabled, the compiler will (as it should!) now shout at you that this isn't defined for empty lists. So you add
group' [] = []
Within pack', we're actually sure the empty case can't happen.
I'm trying to get a function working that uses foldl
to go through a list of tuples and create a string from it.
I'm trying to create a similar function that already works using recursion.
Here is the code that I'm trying to compile:
citeBook :: (String, String, Integer) -> String
citeBook (name, titl, date) = (titl ++ " (" ++ name ++ ", " ++ show date ++ ")\n")
--Recursion function
-- function must be called with putStr in order for newlines to work
bibliography_rec :: [(String, String, Integer)] -> String
bibliography_rec [] = ""
bibliography_rec xs = (citeBook(head xs) ++ bibliography_rec (tail xs))
--foldl function
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold [] = ""
bibliography_fold (x:xs) = foldl (++) citeBook(x) xs --ERROR HERE
So in the very last line of the provided code, I am trying to have foldl
use (++) as the operator in order to combine the strings in the list.
I'm using citeBook(x) as my base case, since x will be the first tuple
taken from the list. Note that citeBook(x) returns a string. Then continue
folding with the list xs.
Here are the errors I'm getting. I think my parameter types for foldl aren't
matching up with what is expected, but everything seems okay to me..
hw1.hs:28:34:
Couldn't match type `[a0]'
with `(String, String, Integer) -> String'
Expected type: ((String, String, Integer) -> String)
-> [a0] -> (String, String, Integer) -> String
Actual type: [a0] -> [a0] -> [a0]
In the first argument of `foldl', namely `(++)'
In the expression: foldl (++) citeBook (x) xs
In an equation for `bibliography_fold':
bibliography_fold (x : xs) = foldl (++) citeBook (x) xs
hw1.hs:28:48:
Couldn't match expected type `[[a0]]'
with actual type `(String, String, Integer)'
In the third argument of `foldl', namely `(x)'
In the expression: foldl (++) citeBook (x) xs
In an equation for `bibliography_fold':
bibliography_fold (x : xs) = foldl (++) citeBook (x) xs
hw1.hs:28:51:
Couldn't match expected type `(String, String, Integer)'
with actual type `[(String, String, Integer)]'
In the fourth argument of `foldl', namely `xs'
In the expression: foldl (++) citeBook (x) xs
In an equation for `bibliography_fold':
bibliography_fold (x : xs) = foldl (++) citeBook (x) xs
I appreciate any and all feedback. Thanks!
You gave foldl the (++) function which has type String -> String -> String. However the collection you're folding over, xs, has type [(String, String, Integer)], not type [String].
You could change bibliography_fold to
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold [] = ""
bibliography_fold (x:xs) = foldl (++) (citeBook x) (map citeBook xs)
or just to
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold xs = foldl (++) "" (map citeBook xs)
but I'm a relative noob at Haskell myself so take my coding style with a grain of salt.
Also, you need to write (citeBook x) and not citeBook(x), or the compiler will assume that citeBook and (x) are both arguments to foldl (correct me if I'm wrong). This helps explain why the error message you got is so strange-looking.
You've already gotten your answer, so I'll provide another way of using a fold to solve this problem:
bibliography_fold :: [(String, String, Integer)] -> String
bibliography_fold = foldr ((++) . citeBook) ""
There's no maps, no special cases, and it can be written in point free style. I'd encourage you to deconstruct this expression in GHCi and inspect each component using :info to explore how it actually works. Look at the types of foldr, ++, citeBook, (++) . citeBook, see if you can figure out why this works. You may want to look up the source code for foldr as well.
I don't get this--
Prelude> "hi"++"there"
"hithere"
Prelude> "hi":"there"
<interactive>:12:6:
Couldn't match expected type `[Char]' with actual type `Char'
Expected type: [[Char]]
Actual type: [Char]
In the second argument of `(:)', namely `"there"'
In the expression: "hi" : "there"
Prelude>
Why doesn't that also return "hithere"?
The types. Try this in GCHi:
Prelude> :t (:)
(:) :: a -> [a] -> [a]
Prelude. :t (++)
(++) :: [a] -> [a] -> [a]
I get it now. The first operator needs to be an element, not a list.
So if i did 'h':"ithere" it would return "hithere"
The operator : is one of the constructors for lists. So "hello" is 'h':'e':'l':'l':'o':[]. You can imagine lists being defined like: (not real haskell syntax)
data List a = (:) a (List a) | []
: constructs a list by taking an element and a list. That's why the type is a->[a]->[a].
++ which concatenates lists is defined by using : as a primitive:
(++) :: [a] -> [a] -> [a]
(++) [] ys = ys
(++) (x:xs) ys = x : xs ++ ys