Ошибка Haskell с функцией, похожей на реплику

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

реплика "the" 3 = "ttthhheee" и реплика "jason" 4 = "jjjjaaaassssoooonnnn"

Вот код, который я написал:

myrepli []  n = []
myrepli [x] 0 = []
myrepli [x] n = (x:(myrepli [x] (n-1)))


repli [] n = []
repli (x:xs) n = (myrepli x n) ++ (repli xs n)

Ошибка, которую я получаю, такова:

Couldn't match expected type `[a]' against inferred type `Char'
     Expected type: [[a]]
     Inferred type: [Char]
   In the first argument of `repli', namely `"jason"'
   In the expression: repli "jason" 3

Как я могу это исправить? Спасибо.


person jason    schedule 18.02.2014    source источник


Ответы (2)


myrepli ожидает список и целое число. Однако определение repli вызывает его с помощью x, что является элементом, а не списком элементов.

Я предполагаю, что вы либо хотите изменить myrepli, чтобы использовать один элемент в качестве аргумента, или вы хотите изменить repli, чтобы передать [x] вместо только x. (Я бы предложил первое, а не второе.)

person MathematicalOrchid    schedule 18.02.2014

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

  • Если в рассматриваемой функции есть сигнатура типа, удалите ее и посмотрите, изменится ли что-нибудь. Если он скомпилируется, спросите у ghci, какой это тип (используя :t). Если он не скомпилируется, по крайней мере, сообщение об ошибке может отличаться достаточно, чтобы дать вам еще одну подсказку.
  • Если подписи типа нет, добавьте ее. Даже если он не скомпилируется, сообщение об ошибке может дать вам еще одну подсказку.
  • Если это не поможет, временно добавьте объявления типов для каждого выражения в функции. (Часто вам нужно разбить некоторые выражения, чтобы увидеть, что происходит на самом деле. Вам также может понадобиться временно включить прагму ScopedTypeVariables.) Снова скомпилируйте и проверьте сообщения об ошибках.

Это последнее упражнение требует больше работы, но я многому научился из этого упражнения. Обычно это точно определяет место, где есть несоответствие между тем, что я думаю о типе, и тем, что GHC думает о типе.

Итак, давайте начнем с добавления сигнатур типов в ваш код:

myrepli :: [a] -> Int -> [a]
myrepli []  n = []
myrepli [x] 0 = []
myrepli [x] n = (x:(myrepli [x] (n-1)))


repli :: [a] -> Int -> [a]
repli [] n = []
repli (x:xs) n = (myrepli x n) ++ (repli xs n) -- triggers a compiler error

А, теперь мы получаем ошибку компилятора:

amy.hs:9:27:
    Couldn't match expected type `a' with actual type `[a]'
      `a' is a rigid type variable bound by
          the type signature for repli :: [a] -> Int -> [a] at amy.hs:7:10
    In the first argument of `myrepli', namely `x'
    In the first argument of `(++)', namely `(myrepli x n)'
    In the expression: (myrepli x n) ++ (repli xs n)

Проблема в вызове myrepli x n. Функция myrepli ожидает список/строку, но вы передаете ей один символ. Измените последнюю строку на:

repli (x:xs) n = (myrepli [x] n) ++ (repli xs n)

В этот момент вы найдете другие ошибки в вашем коде. Но вместо того, чтобы исправлять ваш код, позвольте мне показать вам другой способ сделать это:

repl (x:xs) n = (myReplicate x n) ++ (repl xs n)
repl [] _ = []

-- You could use the library function "replicate" here, but as a
-- learning exercise, we'll write our own.
myReplicate a n = take n (repeat a)
person mhwombat    schedule 18.02.2014
comment
Более короткое окончательное решение: repl xs n = xs >>= (replicate n) (вы можете заменить replicate на myReplicate, если хотите). - person Nicolas; 18.02.2014
comment
Или вы можете использовать бесплатную версию repl = (=<<) . replicate, если вы поменяете порядок аргументов (например, repl 3 "the"). - person bheklilr; 18.02.2014
comment
Судя по вопросу, я подозреваю, что Джейсон немного новичок в Haskell. Возможно, он еще не знал о бесточечной нотации или операторе связывания. - person mhwombat; 18.02.2014
comment
@mhwombat, о вашем первом замечании - забавная вещь в компиляторе haskell заключается в том, что если у вас есть подпись типа для функции, и она выдает ошибку, это не говорит, что ваша подпись неверна, но что ваша программа неверна и принимает подпись правильная! Итак, мы удаляем подписи, чтобы увидеть, компилируется ли он! :-) - person S.R.I; 20.02.2014