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

это ведет себя правильно.