Один знакомый мне программист Clojure недавно заметил, что можно реализовать множество функций последовательности в терминах Clojure reduce
(который является foldl'
Haskell), но, к сожалению, нет способа реализовать (map list xs ys)
(который является zip
Haskell) всего лишь с reduce
.
Я читал об универсальности складок, поэтому почти уверен, что это неправда: конечно zip
возможно с foldr
, и я был бы удивлен, если бы это было невозможно с foldl
. В целях этого обсуждения мы игнорируем проблемы рвения foldl
по сравнению с foldr
: представьте, что у нас неограниченная память и мы работаем только с конечными последовательностями.
Итак, я взял код Haskell, чтобы реализовать zip с помощью foldr, и перевел его на Clojure, выполнив лучше всего скорректировать разницу между foldr и foldl:
(defn zip [xs ys]
(letfn [(done [_] [])
(step [z x]
(fn [ys]
(if (empty? ys)
[]
(conj (z (rest ys)) [x (first ys)]))))]
((reduce step done xs) ys)))
user> (zip [1 2] '[a b c]) ;=> [[1 b] [2 a]]
И действительно, мы получаем пары элементов из xs и ys, составленные вместе, но не по порядку: первый элемент xs соединяется с последним элементом ys, и так далее по строке. Я могу видеть причину проблемы: функция, которую мы производим, потребляет ys, начиная слева, но самое внешнее замыкание (которое называется первым) закрылось над последним элементом xs, поэтому оно не может соединить их справа приказ.
Я думаю, что исправление состоит в том, чтобы каким-то образом выстроить замыкания наизнанку, но я действительно не понимаю, как это сделать. Я с радостью приму решение на Haskell или Clojure.
Я надеюсь на решение формы zip = foldl f x
, так что я могу сказать, что это «просто» сокращение. Конечно, я могу перевернуть один из списков, но тогда он будет выглядеть как zip xs ys = foldl f x xs $ reverse ys
, что не кажется очень удовлетворительным или чистым.