Ну, здесь есть несколько проблем. На верхнем уровне подумайте о сигнатуре zip :: [a] -> [b] -> [(a,b)]
: он не может вернуть [c]
, где c
не является кортежем, поэтому вы не хотите, чтобы zip
был вызовом внешней функции вваш новый zipWith
. Ваша ошибка типа возникает из-за того, что GHC заметил, что он должен заставить c
быть кортежем вещей с элементами, типы которых содержат c
(поскольку f
, примененный к чему-либо, всегда будет иметь тип b -> c
).
Ваше понимание списка также в основном то же самое, что и map f xs
и map f ys
. Второй из них не может проверить тип, поскольку каждый элемент ys
является b
, и вы не можете применить f
к b
(его первый аргумент - a
).
Вместо этого вы могли бы начать с архивирования списков ввода, чтобы получить [(a,b)]
, а затем использовать понимание списка для запуска f
для каждой пары:
zipWith' f xs ys = [f x y | (x,y) <- zip xs ys]
Или с map
и uncurry
вместо понимания списка:
zipWith' f xs ys = map (uncurry f) $ zip xs ys
Или, используя расширение понимания параллельного списка GHC (-XParallelListComp
), явно предназначенное для имитации zip
:
zipWith' f xs ys = [f x y | x <- xs | y <- ys]
Asупомянутое выше, вы действительно не можете сделать zip
last , так как он будет производить кортежи. Вы можете сделать что-то вроде
zipWith' f xs ys = [fx y | (fx, y) <- zip [f x | x <- xs] ys]
, которое применяет f
к элементам первого списка (в [f x | x <- xs]
или, альтернативно, map f xs
), объединяет этот список частично примененных функций со спискомвторые аргументы, а затем применяют частичные функции к соответствующим им вторым аргументам во внешнем понимании, но это немного обходной способ сделать это.