Как определить zipWith, используя zip и понимание списка - PullRequest
2 голосов
/ 17 октября 2019

Я пытаюсь написать функцию zipWith, используя zip и понимание списка. Мне нужно сжать два списка после применения функции. Однако я не знаю, где использовать понимание списка.

Я пытался сделать два понимания списка для каждого списка.

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]

Я ожидаю, что функция будет идентична функции zipWith, однако она не загружается и выдает ошибку:

Occurs check: cannot construct the infinite type:
        c ~ (b -> c, b -> c)
      Expected type: [c]
        Actual type: [(b -> c, b -> c)]
    • In the expression: zip [f x | x <- xs] [f y | y <- ys]
      In an equation for ‘zipWith'’:
          zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]

    • Relevant bindings include
        ys :: [b] (bound at tutorial5.hs:166:15)
        f :: a -> b -> c (bound at tutorial5.hs:166:10)
        zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
          (bound at tutorial5.hs:166:1)
    |
166 | zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]

1 Ответ

2 голосов
/ 17 октября 2019

Ну, здесь есть несколько проблем. На верхнем уровне подумайте о сигнатуре 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), объединяет этот список частично примененных функций со спискомвторые аргументы, а затем применяют частичные функции к соответствующим им вторым аргументам во внешнем понимании, но это немного обходной способ сделать это.

...