Reputation: 103
I am writing a function to expand a String
Example:
foo "a4b4"
should give back:
"aaaabbbb"
Here is my code:
foo :: String -> String
foo (x:xs) = let curent = fooHelp(x, read $ charToString( xs !! 0 ) :: Int)
in x : (curent) ++ foo (tail xs)
fooHelp:
fooHelp :: String -> Int -> String
fooHelp x n
| n >= 3 = x ++ fooHelp x (n - 1)
| n == 2 = x
| n == 1 = ""
and charToString:
charToString :: Char -> String
charToString c = [c]
it takes x and appends it to the current. In current fooHelp will give back expanded String
example : foo "a4b4"
then x = "a"
, xs = "4b4"
, xs !! 0 = '4'
read $ charToString( xs !! 0 ) :: Int)
will convert char '4'
to int 4
and pass it to fooHelp alongside with x("a") -> fooHelp(x, 4)
and give back "aaa". Then x : current
should give back "aaaa"
because x = "a"
and current "aaa"
and then recursive call with ++ foo tail xs
where xs ="b4"
and it should repeat the process.
I am getting the error:
test.hs:173:34: error:
• Couldn't match type ‘(Char, Int)’ with ‘[Char]’
Expected type: String
Actual type: (Char, Int)
• In the first argument of ‘fooHelp’, namely
‘(x, read $ charToString (xs !! 0) :: Int)’
In the expression:
fooHelp (x, read $ charToString (xs !! 0) :: Int)
In an equation for ‘curent’:
curent = fooHelp (x, read $ charToString (xs !! 0) :: Int)
|
173 | foo (x:xs) = let curent = fooHelp(x, read $ charToString( xs !! 0 ) :: Int)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Where did I make mistake? I tested function fooHelp and it works fine with such arguments as in foo.
Testing fooHelp:
xs = "4b4"
test = read $ charToString( xs !!0 ) :: Int
*Main> test
4
Upvotes: 1
Views: 716
Reputation: 477190
A classic mistake is to call a function like f (x1, x2)
. In Haskell functions have one parameter, and often this is not a tuple.
Your fooHelper
function has type:
fooHelp :: String -> (Int -> String)
so it is a function that takes a String
, and the returns a function that maps Int
s on String
s. You thus should call the function like:
(fooHelp x) (read $ charToString( xs !! 0 ) :: Int)
or less verbose:
(fooHelp x) (read $ charToString( xs !! 0 ) :: Int)
But now the types still not match: x
is a Char
, not a String
, you can wrap it in a list, like:
fooHelp [x] (read $ charToString( xs !! 0 ) :: Int)
like:
foo :: String -> String
foo (x:xs) = let curent = fooHelp [x] (read $ charToString( xs !! 0 ) :: Int)
in x : (curent) ++ foo (tail xs)
But still this function has a problem: we iterate over the string, so eventually we will reach an empty string, and foo
has no case for that. We need to return the empty string in that case, like:
foo :: String -> String
foo (x:xs) = let curent = fooHelp [x] (read $ charToString( xs !! 0 ) :: Int)
in x : (curent) ++ foo (tail xs)
foo [] = ""
But it is still not very elegant. We here perform a lot of unnecessary operations, like wrapping characters to strings, etc.
We can make use of the replicate :: Int -> a -> [a]
function to repeat a character a given number of times. For example:
Prelude> replicate 3 'a'
"aaa"
Furthermore the digitToInt :: Char -> Int
function can parse a digit character to the corresponding Int
:
Prelude Data.Char> digitToInt '3'
3
So we here can use these two to each time take the first two characters of the string, and use replicate (digitToInt k) x
to generate a string where x
si repeated the requested amount of time, and perform recursion on the rest of the string, like:
import Data.Char(digitToInt)
foo :: String -> String
foo (x:k:xs) = replicate (digitToInt k) x ++ foo xs
foo _ = ""
Upvotes: 4
Reputation: 57590
The expression fooHelp(x, read $ charToString( xs !! 0 ) :: Int)
tries to pass a single argument — a (Char, Int)
pair — to fooHelp
, which is wrong. Presumably, you want to instead write:
fooHelp (charToString x) (read $ charToString( xs !! 0 ) :: Int)
Upvotes: 4