0

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.

Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • 1
    just to make sure: you may only use `length` and have to do it all using recursion and pattern matching on the list of characters? – Random Dev May 19 '15 at 04:42
  • the error in your code is easily explained: when your second parameter (the list `[a]`) is empty `[]` you cannot match it against `(x:xs)` so you have to include a *case* for it (like `makeString val [] = ...`) in a separate line – Random Dev May 19 '15 at 04:44
  • also your `otherwise` case seems strange - why would you return the complete string if your `val` = 0 - don't you want the empty string there instead? – Random Dev May 19 '15 at 04:45
  • as an additional tipp: as you see you run out of characters because you *drop* the `x` from `(x:xs)` - maybe you want to *save* them for later use somewhere (where could this be ... *hint*: you want to somehow give them to the recursive call to `makeString` for *later* use ;) ) – Random Dev May 19 '15 at 04:46
  • Yes, i am only allowed to use "length" and have to do it all using recursion and pattern matching. And as for your second response, i need to handle such a case separately? Is it possible if i can load my first list again and repeat doing it? Edit: I see what you are trying to say. Thank you! :) – Ahmed Zaidi May 19 '15 at 04:47
  • you cannot *load* the list from somewhere - you have to use what is given to the function - aka it's parameters – Random Dev May 19 '15 at 04:47
  • btw: I think you are on the right track to get `makeString` to *repeat* the characters, you just have to figure out the details I mentioned – Random Dev May 19 '15 at 04:48
  • Are you allowed to use `let` or `where`? – Zeta May 19 '15 at 07:21
  • @AhmedZaidi in case you don't have any more questions you should probably either answer it yourself or close/delete the question - just to keep the Haskell tag tidy ;) – Random Dev May 19 '15 at 07:44
  • @Zeta yes i am allowed to use `where` but not let. We havent gotten that far in class. – Ahmed Zaidi May 19 '15 at 16:38
  • @CarstenKönig i was actually thinking the same but since i am new to this site, i am still exploring all the features. Cant seem to find how to make a question from "unanswered" to "answered". – Ahmed Zaidi May 19 '15 at 16:59
  • @AhmedZaidi: You probably want to read https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work. – Zeta Jan 13 '16 at 21:24

3 Answers3

1

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]
Gabriel Ciubotaru
  • 1,042
  • 9
  • 22
1

Like Carsten said, you should

  • handle the case when the list is empty
  • push the first element at the end of the list when you drop it.
  • return an empty list when n is 0 or lower

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"
Damien
  • 300
  • 1
  • 8
0

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.

Zeta
  • 103,620
  • 13
  • 194
  • 236