Реализация программы, в которой символы строки повторяются определенное время в haskell

Это вопрос из моей домашней работы, поэтому советы будут весьма полезны.

Я изучаю Haskell в этом семестре, и мое первое назначение требует от меня написать функцию, которая вводит 2 строки (string1 а также string2) и возвращает строку, состоящую из (повторяющихся) символов первой строки string1 пока строка такой же длины, как string2 был создан.

Мне разрешено использовать только функцию Prelude length,

Например: принять как string1"Key" и мое имя "Ahmed" как string2 функция должна вернуться "KeyKe",

Вот что у меня так далеко:

    makeString :: Int -> [a] -> [a]
    makeString val (x:xs)
        | val > 0 = x : makeString (val-1) xs
        | otherwise = x:xs

Вместо того, чтобы напрямую давать ему две строки, я даю ему целочисленное значение (так как позже я могу заменить его на длину), но это дает мне ошибку времени выполнения:

*Main> makeString 8 "ahmed"

"ahmed*** Exception: FirstScript.hs: (21,1)-(23,21) : Non-exhaustive patterns in function makeString

Я думаю, что это может что-то сделать, чтобы мой список исчерпал себя и стал пустым списком (?).

Небольшая помощь будет высоко ценится.

3 ответа

Решение

Я думаю, что этого кода достаточно, чтобы решить вашу проблему:

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)

extend' функция будет циклически повторять первую строку до тех пор, пока она не будет израсходована, а затем начнет использовать ее снова

Вы также можете сделать это с помощью take а также cycle подобные функции:

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)

или более общая версия

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)

который способен принимать списки любого типа:

>extend [1,2,3,4] "foo bar"
[1,2,3,4,1,2,3]

Как сказал Карстен, ты должен

  • обрабатывать случай, когда список пуст
  • нажмите первый элемент в конце списка, когда вы его опускаете.
  • вернуть пустой список, когда n равно 0 или ниже

Например:

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 ""

попробуем это в ghci:

>makeString (length "Ahmed") "Key"
"KeyKe"

Примечание: этот ответ написан на грамотном языке Haskell. Сохранить как Filename.lhs и попробуйте в GHCi.

я думаю что length это красная сельдь в этом случае. Вы можете решить эту проблему исключительно с помощью рекурсии и сопоставления с образцом, которые будут работать даже с очень длинными списками. Но обо всем по порядку.

Какой тип должен иметь наша функция? Мы берем две строки, и мы будем повторять первую строку снова и снова, что звучит как String -> String -> String, Однако эта вещь "повторять снова и снова" на самом деле не уникальна для строк: вы можете делать это со всеми видами списков, поэтому мы выбираем следующий тип:

> repeatFirst :: [a] -> [b] -> [a]
> repeatFirst as bs = go as bs

Хорошо, пока ничего необычного не произошло, верно? Мы определили repeatFirst с точки зрения go, который до сих пор отсутствует. В go мы хотим обменять предметы bs с соответствующими пунктами as Итак, мы уже знаем базовый случай, а именно, что должно произойти, если bs пустой:

>    where go  _     []     = []

Что, если bs не пусто? В этом случае мы хотим использовать правильный элемент из as, Таким образом, мы должны пройти оба одновременно:

>          go (x:xs) (_:ys) = x : go xs ys

В настоящее время мы обрабатываем следующие случаи: пустой список второго аргумента и непустые списки. Нам все еще нужно обработать пустой список первого аргумента:

>          go []     ys     = 

Что должно произойти в этом случае? Ну, нам нужно начать снова с as, И действительно, это работает:

>                              go as ys

Вот снова все в одном месте:

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

Обратите внимание, что вы могли бы использовать cycle, zipWith а также const вместо этого, если у вас не было ограничений:

repeatFirst :: [a] -> [b] -> [a]
repeatFirst = zipWith const . cycle

Но это, вероятно, для другого вопроса.

Другие вопросы по тегам