Reputation: 15
This is a question from my homework thus tips would be much likely appreciated.
I am learning Haskell this semester and my first assignment requires me to write a function that inputs 2 string (string1
and string2
) and returns a string that is composed of (the repeated) characters of first string string1
until a string of same length as string2
has been created.
I am only allowed to use the Prelude function length
.
For example: take as string1
"Key"
and my name "Ahmed"
as string2
the function should return "KeyKe"
.
Here is what I've got so far:
makeString :: Int -> [a] -> [a]
makeString val (x:xs)
| val > 0 = x : makeString (val-1) xs
| otherwise = x:xs
Instead of directly giving it two strings i am giving it an integer value (since i can subtitute it for length later on), but this is giving me a runtime-error:
*Main> makeString 8 "ahmed"
"ahmed*** Exception: FirstScript.hs: (21,1)-(23,21) : Non-exhaustive patterns in function makeString
I think it might have something to do my list running out and becoming an empty list(?).
A little help would be much appreciated.
Upvotes: 0
Views: 252
Reputation: 105905
Note: This answer is written in literate Haskell. Save it as Filename.lhs
and try it in GHCi.
I think that length
is a red herring in this case. You can solve this solely with recursion and pattern matching, which will even work on very long lists. But first things first.
What type should our function have? We're taking two strings, and we will repeat the first string over and over again, which sounds like String -> String -> String
. However, this "repeat over and over" thing isn't really unique to strings: you can do that with every kind of list, so we pick the following type:
> repeatFirst :: [a] -> [b] -> [a]
> repeatFirst as bs = go as bs
Ok, so far nothing fancy happened, right? We defined repeatFirst
in terms of go
, which is still missing. In go
we want to exchange the items of bs
with the corresponding items of as
, so we already know a base case, namely what should happen if bs
is empty:
> where go _ [] = []
What if bs
isn't empty? In this case we want to use the right item from as
. So we should traverse both at the same time:
> go (x:xs) (_:ys) = x : go xs ys
We're currently handling the following cases: empty second argument list, and non-empty lists. We still need to handle the empty first argument list:
> go [] ys =
What should happen in this case? Well, we need to start again with as
. And indeed, this works:
> go as ys
Here's everything again at a single place:
repeatFirst :: [a] -> [b] -> [a]
repeatFirst as bs = go as bs
where go _ [] = []
go (x:xs) (_:ys) = x : go xs ys
go [] ys = go as ys
Note that you could use cycle
, zipWith
and const
instead if you didn't have constraints:
repeatFirst :: [a] -> [b] -> [a]
repeatFirst = zipWith const . cycle
But that's probably for another question.
Upvotes: 0
Reputation: 300
Like Carsten said, you should
For example:
makeString :: Int -> [a] -> [a]
makeString _ [] = [] -- makeString 10 "" should return ""
makeString n (x:xs)
| n > 0 = x:makeString (n-1) (xs++[x])
| otherwise = [] -- makeString 0 "key" should return ""
trying this in ghci :
>makeString (length "Ahmed") "Key"
"KeyKe"
Upvotes: 1
Reputation: 1092
I think this code is enough to solve your problem:
extend :: String -> String -> String
extend src dst = extend' src src (length dst)
where
extend' :: String -> String -> Int -> String
extend' _ _ 0 = []
extend' [] src size = extend' src src size
extend' (x:xs) src size = x : extend' xs src (size - 1)
The extend'
function will cycle the first string until is is consumed then will begin to consume it again.
You can also make it using take
and cycle
like functions:
repeatString :: String -> String
repeatString x = x ++ repeatString x
firstN :: Int -> String -> String
firstN 0 _ = []
firstN n (x:xs) = x : firstN ( n - 1 ) xs
extend :: String -> String -> String
extend src dst = firstN (length dst) (repeatString src)
or a more generic version
repeatString :: [a] -> [a]
repeatString x = x ++ repeatString x
firstN :: (Num n, Eq n ) => n -> [a] -> [a]
firstN 0 _ = []
firstN n (x:xs) = x : firstN ( n - 1 ) xs
extend :: [a] -> [b] -> [a]
extend _ [] = error "Empty target"
extend [] _ = error "Empty source"
extend src dst = firstN (length dst) (repeatString src)
which is capable of taking any type of lists:
>extend [1,2,3,4] "foo bar"
[1,2,3,4,1,2,3]
Upvotes: 1