Есть ли эквивалент для функции Zip в Clojure Core или Contrib?
В Clojure я хочу объединить два списка, чтобы получить список пар,
> (zip '(1 2 3) '(4 5 6))
((1 4) (2 5) (3 6))
В Haskell или Ruby функция называется zip. Реализовать это не сложно, но я хотел убедиться, что я не пропустил функцию в Core или Contrib.
В Core есть пространство имен zip, но оно описывается как предоставление доступа к функциональной технике Zipper, которая, похоже, не является тем, чем я занимаюсь.
Есть ли эквивалентная функция для объединения 2 или более списков, таким образом, в Core?
Если нет, это потому, что существует идиоматический подход, который делает функцию ненужной?
7 ответов
(map vector '(1 2 3) '(4 5 6))
делает то, что вы хотите:
=> ([1 4] [2 5] [3 6])
Хаскеллу нужна коллекция zipWith
(zipWith3
, zipWith4
,...) функции, потому что все они должны быть определенного типа; в частности, число входных списков, которые они принимают, должно быть фиксированным. (The zip
, zip2
, zip3
... семью можно рассматривать как специализацию zipWith
семья для общего случая использования tupling).
В отличие от этого, Clojure и другие Лиспы имеют хорошую поддержку функций переменной арности; map
является одним из них и может быть использован для "кортежа" способом, аналогичным Haskell's
zipWith (\x y -> (x, y))
Идиоматический способ построения "кортежа" в Clojure - это создание короткого вектора, как показано выше.
(Просто для полноты обратите внимание, что Haskell с некоторыми базовыми расширениями допускает функции переменной arity; хотя для их использования требуется хорошее понимание языка, и ванильный Haskell 98, вероятно, вообще их не поддерживает, поэтому предпочтительны фиксированные функции arity. для стандартной библиотеки.)
(partition 2 (interleave '(1 2 3) '(4 5 6)))
=> ((1 4) (2 5) (3 6))
или в более общем плане
(defn zip [& colls]
(partition (count colls) (apply interleave colls)))
(zip '( 1 2 3) '(4 5 6)) ;=> ((1 4) (2 5) (3 6))
(zip '( 1 2 3) '(4 5 6) '(2 4 8)) ;=> ((1 4 2) (2 5 4) (3 6 8))
Чтобы дать вам именно то, что вы хотели, картирование list
через два списка вы получите список списков, как в вашем примере. Я думаю, что многие Clojurians будут склонны использовать векторы для этого, хотя это будет работать с чем угодно. и входы не должны быть одного типа. map создает из них seqs, а затем отображает seqs так, что любой ввод seq'able будет работать нормально.
(map list '(1 2 3) '(4 5 6))
(map list [1 2 3] '(4 5 6))
(map hash-map '(1 2 3) '(4 5 6))
(map hash-set '(1 2 3) '(4 5 6))
Встроенным способом будет просто функция interleave:
(interleave [1 2 3 4] [5 6 7 8]) => [1 5 2 6 3 7 4 8]
#(применить список карт%) транспонирует матрицу так же, как функция Python zip*. Как определение макроса:
user => (defmacro py-zip [lst] `(применить список карт ~ lst))
# 'Пользователь / ая-молния
user => (py-zip '((1 2 3 4) (9 9 9 9) (5 6 7 8)))
((1 9 5) (2 9 6) (3 9 7) (4 9 8))
user => (py-zip '((1 9 5) (2 9 6) (3 9 7) (4 9 8)))
((1 2 3 4) (9 9 9 9) (5 6 7 8))
Существует функция с именем zipmap, которая может иметь аналогичный эффект, (zipmap (1 2 3)
(4 5 6)) Выходной сигнал выглядит следующим образом:
{3 6, 2 5, 1 4}