Existe um equivalente para a função Zip no Clojure Core ou Contrib?

130

No Clojure, quero combinar duas listas para fornecer uma lista de pares,

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

Em Haskell ou Ruby, a função é chamada zip . Implementá-lo não é difícil, mas eu queria ter certeza de que não estava perdendo uma função no Core ou no Contrib.

Há um espaço para nome zip no Core, mas é descrito como fornecendo acesso à técnica funcional do Zipper, que não parece ser o que estou procurando.

Existe uma função equivalente para combinar 2 ou mais listas, dessa maneira, no Core?

Se não houver, é porque existe uma abordagem idiomática que torna a função desnecessária?

John Kane
fonte
Há uma zipfunção na biblioteca Tupelo: cloojure.github.io/doc/tupelo/tupelo.core.html#var-zip
Alan Thompson

Respostas:

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

faz o que você quer:

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

Haskell precisa de uma coleção de zipWith( zipWith3, zipWith4...) funções, porque todos precisam ser de um determinado tipo ; em particular, o número de listas de entrada que eles aceitam precisa ser corrigido. (A zip, zip2, zip3, ... família pode ser considerada como uma especialização da zipWithfamília para o caso de uso comum de tupling).

Por outro lado, Clojure e outros Lisps têm um bom suporte para funções variáveis ​​de aridade; mapé um deles e pode ser usado para "tupling" de maneira semelhante à de Haskell

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

A maneira idiomática de criar uma "tupla" no Clojure é construir um vetor curto, conforme exibido acima.

(Apenas para completar, observe que o Haskell com algumas extensões básicas permite funções variáveis ​​de aridade; no entanto, usá-las requer uma boa compreensão da linguagem, e o Haskell 98 baunilha provavelmente não as suporta, portanto, as funções de aridade fixa são preferíveis para a biblioteca padrão.)

Michał Marczyk
fonte
8
Observe que isso se comporta de maneira diferente do que zipquando as coleções não têm o mesmo comprimento. Ruby continuará processando e fornecendo nils para a coleção mais curta, enquanto Clojure interromperá o processamento assim que uma das coleções estiver esgotada.
Nate W.
@NateW. É bom notar isso, obrigado. Em Haskell, zipcomporta-se como o de Clojure a mapesse respeito.
Michał Marczyk
1
Posso pedir uma referência sobre funções Haskell de variável variável?
Teodor
22
(partition 2 (interleave '(1 2 3) '(4 5 6))) 
=> ((1 4) (2 5) (3 6))

ou mais geralmente

(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
fonte
12
(map vector [1 2 3] [4 5 6])
danlei
fonte
11

para fornecer exatamente o que você queria, o mapeamento listnas duas listas fornecerá uma lista de listas como no seu exemplo. Eu acho que muitos clojurianos tenderiam a usar vetores para isso, embora funcione com qualquer coisa. e as entradas não precisam ser do mesmo tipo. O mapa cria seqs a partir deles e mapeia os seqs para que qualquer entrada seq'able funcione bem.

(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))
Arthur Ulfeldt
fonte
1
Eu acho que você quer dizer hash-map e hash-set em vez de map and set.
amigos estão dizendo sobre cgrand
3

A maneira integrada seria simplesmente a função 'intercalar':

(interleave [1 2 3 4] [5 6 7 8]) => [1 5 2 6 3 7 4 8]
lsh
fonte
6
para atingir a meta do OP você deve adicionar(partition 2 (interleave [1 2 3 4][5 6 7 8]))
skuro 15/08/2012
sim - parece que não olhei de perto a saída desejada do OP.
Lsh
-1

Existe uma função chamada zipmap, que pode ter o efeito semelhante (zipmap (1 2 3)(4 5 6)) A saída é como fluxos: {3 6, 2 5, 1 4}

温 发 琥
fonte
O zipmap retorna um mapa para você, que não garante o pedido
Ilya Shinkarenko
-2

# (apply map list%) transpõe uma matriz como a função zip * do Python. Como uma definição de macro:

user => (defmacro py-zip [lst] `(aplica lista de mapas ~ lst))

# 'usuário / py-zip

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
fonte
Como isso é melhor do que apenas usar 'map'? E qual é o objetivo de uma macro aqui?
Dave Newton