Есть ли эквивалент функции Zip в Clojure Core или Contrib?

130

В Clojure я хочу объединить два списка, чтобы получить список пар,

> (zip '(1 2 3) '(4 5 6))  
((1 4) (2 5) (3 6))

В Haskell или Ruby функция называется zip . Реализовать это несложно, но я хотел убедиться, что не пропустил ни одной функции в Core или Contrib.

Есть молния Core пространство имен , но оно описано как обеспечивающее доступ к функциональной технике Zipper, которая, похоже, не то, что мне нужно.

Есть ли в Core эквивалентная функция для объединения 2 или более списков таким образом?

Если нет, то это потому, что существует идиоматический подход, который делает функцию ненужной?

Джон Кейн
источник
В zipбиблиотеке Tupelo есть функция: cloojure.github.io/doc/tupelo/tupelo.core.html#var-zip
Алан Томпсон

Ответы:

220
(map vector '(1 2 3) '(4 5 6))

делает то, что вы хотите:

=> ([1 4] [2 5] [3 6])

Haskell необходим набор zipWith( zipWith3, zipWith4, ...) функции, потому что они все должны быть определенного типа ; в частности, необходимо зафиксировать количество принимаемых списков ввода. (The zip, zip2, zip3... семья может рассматриваться как специализацияzipWith семьи для общего пользования случае кортежей).

Напротив, Clojure и другие Lisp хорошо поддерживают функции переменной арности; mapявляется одним из них и может использоваться для "кортежа" аналогично Haskell's

zipWith (\x y -> (x, y))

Идиоматический способ создания «кортежа» в Clojure - это построить короткий вектор, как показано выше.

(Для полноты картины обратите внимание, что Haskell с некоторыми базовыми расширениями действительно допускает функции переменной арности; однако их использование требует хорошего понимания языка, а ванильный Haskell 98, вероятно, не поддерживает их вообще, поэтому функции с фиксированной арностью предпочтительнее для стандартной библиотеки.)

Михал Марчик
источник
8
Обратите внимание, что это ведет себя иначе, чем zipкогда коллекции имеют разную длину. Ruby продолжит обработку и предоставит nils для более короткой коллекции, тогда как Clojure прекратит обработку, как только одна из коллекций будет исчерпана.
Nate W.
@NateW. Это хорошо, спасибо. В этом отношении Haskell zipведет себя как Clojure map.
Michał Marczyk
1
Могу я попросить справку о функциях Haskell с переменной arity?
Теодор
23
(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))
Lambder
источник
11

чтобы дать вам именно то, что вы хотели, сопоставление listдвух списков даст вам список списков, как в вашем примере. Я думаю, что многие клоджурцы склонны использовать для этого векторы, хотя это будет работать с чем угодно. и входы не обязательно должны быть одного типа. map создает из них последовательности, а затем сопоставляет их, поэтому любой ввод с последовательностью будет работать нормально.

(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))
Артур Ульфельдт
источник
1
Я думаю, вы имеете в виду hash-map и hash-set вместо map and set.
cgrand 07
3

Встроенный способ - это просто функция interleave:

(interleave [1 2 3 4] [5 6 7 8]) => [1 5 2 6 3 7 4 8]
LSH
источник
6
для достижения цели ОП вы должны добавить(partition 2 (interleave [1 2 3 4][5 6 7 8]))
skuro
да - похоже, я не слишком внимательно смотрел на желаемый результат OP.
lsh 05
-1

Существует функция под названием zipmap, которая может иметь аналогичный эффект (zipmap (1 2 3)(4 5 6)). Вывод выглядит следующим образом: {3 6, 2 5, 1 4}

温 发 琥
источник
zipmap возвращает вам карту, которая не гарантирует порядок
Илья Шинкаренко
-2

# (применить список карт%) транспонирует матрицу точно так же, как функция 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))

dhr bibberknie
источник
Как это лучше, чем просто использовать «карту»? И какой здесь смысл макроса?
Дэйв Ньютон