Haskell реализация" zip " странная ошибка
у меня есть следующая реализация функции zip в haskell
myzip (a:b) (z:g)
| b == [] = []
| g == [] = []
| otherwise = (a,z) : myzip b g
когда я загружаю его в ghci, я получаю следующую ошибку
No instance for (Eq b)
arising from a use of `=='
In the expression: g == []
In a stmt of a pattern guard for
an equation for `myzip':
g == []
In an equation for `myzip':
myzip (a : b) (z : g)
| b == [] = []
| g == [] = []
| otherwise = (a, z) : myzip b g
сбой, загруженные модули: нет.
Я не уверен, почему это не работает. Кто-нибудь может мне помочь?
1 ответов
на самом деле функция, которую вы дали в вопросе, компилируется отлично. Вы б получите ошибку, которую вы процитировали, если вместо этого у вас было:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
| b == [] = []
| g == [] = []
| otherwise = (a, z) : myzip b g
с явной сигнатурой типа, говорящей, что myzip
работы по списку любой типы a
и b
. Но вы использовали b == []
и g == []
. Оператор равенства не определенный на любом типе, только на типах которые член Eq
введите класс, так что код у вас есть написанное не соответствует типу, который вы дали.
это то, что сообщение об ошибке говорит довольно прямо, но если вы только учитесь и еще не дошли до классов типа, то это немного неясно.
если вы измените подпись типа myzip
говорил, что a
и b
должны быть членами Eq
тип класса, а затем код, который вы дали будет работать:
myzip :: (Eq a, Eq b) => [a] -> [b] -> [(a, b)]
или если вы оставите подпись типа полностью выключенной (как вы сделали в вопрос), GHC фактически выводит этот тип из того факта, что вы использовали ==
оператор, и код просто компилируется как есть.
однако проверка того, является ли список пустым, может быть выполнена без использования ==
оператор, так что вы можете написать myzip
так что он действительно работает на любых типах a
и b
. Один из способов-использовать null
функция:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
| null b = []
| null g = []
| otherwise = (a, z) : myzip b g
но гораздо более распространенный способ-это просто использовать несколько уравнений для определения myzip
, С основанием случаи, соответствующие шаблону []
и основной случай, который предполагает, что списки непустые:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:[]) _ = []
myzip _ (z:[]) = []
myzip (a:b) (z:g) = (a, z) : myzip b g
обратите внимание, что этот стиль также сделал очевидным, что в вашей реализации есть ошибка. Ты выбрасываешь последнее a
или z
, и нет никакого случая, когда списки полностью пусты!
когда ваше уравнение говорит myzip (a:b) (z:g)
и затем проверил b
и g
против пустого списка, это было на самом деле слишком поздно проверять не то. Вам не нужно проверять, если b
is []
, вам нужно проверить, является ли весь список был пуст. Но вы уже предположили, что он не пуст, и разложили его на a:b
. Это приводит к тому, что ваш код (a) возвращает неправильный результат, потому что он отбрасывает последнюю пару элементов, которые он должен сжимать, и (b) создает ошибку, когда один из аргументов является пустым списком.
рекурсия в списках обычно больше похожа это:
myzip :: [a] -> [b] -> [(a, b)]
myzip [] _ = []
myzip _ [] = []
myzip (a:b) (z:g) = (a, z) : myzip b g
это ведет себя правильно.