#haskell #types #typeclass
#haskell #типы #класс типов
Вопрос:
Я новичок в Haskell. Я написал этот код:
deleteDuplicates :: [a] -> [a]
deleteDuplicates [] = []
deleteDuplicates (x:xs)
| x == (head xs) = x : (deleteDuplicates (tail xs))
| otherwise = x : (head xs) : (deleteDuplicates (tail xs))
Что означает эта ошибка и почему она возникла? Я случайно как-то сравниваю два разных типа?
set2.hs:10:3:
Could not deduce (Eq a) from the context ()
arising from a use of `==' at set2.hs:10:3-16
Possible fix:
add (Eq a) to the context of
the type signature for `deleteDuplicates'
In the expression: x == (head xs)
In a stmt of a pattern guard for
the definition of `deleteDuplicates':
x == (head xs)
In the definition of `deleteDuplicates':
deleteDuplicates (x : xs)
| x == (head xs) = x : (deleteDuplicates (tail xs))
| otherwise = x : (head xs) : (deleteDuplicates (tail xs))
Ответ №1:
Ваша подпись типа неверна:
deleteDuplicates :: [a] -> [a]
Это говорит о том, что ваша функция может работать со списками любого типа, a
но это неправда! Позже вы вызываете:
x == (head xs)
Таким образом, вы должны иметь возможность сравнивать свой тип для равенства. Это означает, что подпись должна быть:
deleteDuplicates :: Eq a => [a] -> [a]
В такие моменты полезно удалить вашу явную подпись типа, загрузить функцию в GHCi и выяснить, какой тип, по мнению интерпретатора, она должна иметь (через :t deleteDuplicates
).
Еще ошибки
Кроме того, ваше использование head
there — плохая идея. head
является частичной функцией и завершится ошибкой, когда xs == []
. Я предлагаю вам расширить соответствие шаблону:
deleteDuplicates (x1:x2:xs)
| x1 == x2 = ...
| otherwise = x1 : deleteDuplicates (x2:xs)
Также обратите внимание на исправление в otherwise
случае. Вы пропустили x2, но что, если x2 соответствует следующему элементу в списке? Что-то подобное [1,2,2,3]
было бы обнаружено 1 /= 2
, и тогда рекурсия получила бы список [2,3]
— упс!
Комментарии:
1. Вы полностью правы насчет чего-то вроде
[1, 2, 2, 3]
неспособности создать список со всеми удаленными дубликатами. Виноват.Eq a =>
Для меня это ново … значит, единственное, что нужно сделать, это обеспечить, чтобы элементы типаa
были сопоставимы?2. Питер: В принципе, да. Это «ограничение класса типов». Функция
==
объявлена вEq
классе типов, поэтому ее можно использовать только в экземплярах Eq.3. @Pieter: сопоставимо по равенству, да.
Eq a =>
говорит, чтоa
это должен быть экземпляр класса typeEq
. Класс типа определяет интерфейс, который реализует экземпляр.Eq
Класс предоставляет две функции,(==)
и(/=)
, обе с типомa -> a -> Bool
. Другие классы типов будут предоставлять другие функции, напримерOrd
предоставляет тесты с меньшим, большим, чем и аналогичные тесты упорядочения.
Ответ №2:
Компилятор сообщает вам, что только из сигнатуры типа не может быть определено, a
является ли это instanceof Eq
.
deleteDuplicates :: Eq a => [a] -> [a]
Вам нужно сообщить компилятору, что a
это экземпляр класса типа Eq, упомянув его в подписи.
Ответ №3:
Выражение
x == (head xs)
требуется, чтобы ==
было определено для членов xs
, что означает a
, что это должен быть экземпляр класса типов Eq
. Измените сигнатуру типа вашей функции на
deleteDuplicates :: Eq a => [a] -> [a]