Czy istnieje odpowiednik funkcji Zip w Clojure Core lub Contrib?

130

W Clojure chcę połączyć dwie listy, aby uzyskać listę par,

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

W Haskell lub Ruby funkcja nazywa się zip . Wdrożenie go nie jest trudne, ale chciałem się upewnić, że nie brakuje mi funkcji w Core lub Contrib.

W Core istnieje przestrzeń nazw zip , ale jest opisywana jako zapewniająca dostęp do techniki funkcjonalnej Zipper, która nie wydaje się być tym, czego szukam.

Czy istnieje równoważna funkcja łączenia 2 lub więcej list w ten sposób w Core?

Jeśli nie, to czy dzieje się tak dlatego, że istnieje idiomatyczne podejście, które sprawia, że ​​funkcja jest niepotrzebna?

John Kane
źródło
W zipbibliotece Tupelo jest funkcja: cloojure.github.io/doc/tupelo/tupelo.core.html#var-zip
Alan Thompson

Odpowiedzi:

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

robi co chcesz:

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

Haskell potrzebuje kolekcję zipWith( zipWith3, zipWith4...) funkcjonuje, bo wszystko trzeba być określonego typu ; w szczególności należy ustalić liczbę akceptowanych przez nie list wejściowych. (The zip, zip2, zip3, ... rodzina może być traktowane jako specjalizacji zipWithrodziny do wspólnej przypadku użycia z tupling).

W przeciwieństwie do tego Clojure i inne Lispy mają dobre wsparcie dla funkcji zmiennej arity; mapjest jednym z nich i może być używany do „podkręcania” w sposób podobny do Haskella

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

Idiomatycznym sposobem budowania „krotki” w Clojure jest skonstruowanie krótkiego wektora, jak pokazano powyżej.

(Dla kompletności, zwróć uwagę, że Haskell z kilkoma podstawowymi rozszerzeniami dopuszcza zmienne funkcje aryczności; używanie ich wymaga jednak dobrego zrozumienia języka, a waniliowy Haskell 98 prawdopodobnie w ogóle ich nie obsługuje, dlatego preferowane są funkcje o ustalonej liczbie znaków dla biblioteki standardowej.)

Michał Marczyk
źródło
8
Zauważ, że zachowuje się to inaczej niż zipwtedy, gdy kolekcje nie są tej samej długości. Ruby będzie kontynuować przetwarzanie i dostarczać nilpliki do krótszej kolekcji, podczas gdy Clojure przestanie przetwarzać, gdy jedna z kolekcji zostanie wyczerpana.
Nate W.
@NateW. Dobrze to zauważyć, dzięki. In Haskell zipzachowuje się mappod tym względem jak Clojure's .
Michał Marczyk
1
Czy mogę prosić o odniesienie do funkcji Haskella o zmiennej arity?
Teodor
23
(partition 2 (interleave '(1 2 3) '(4 5 6))) 
=> ((1 4) (2 5) (3 6))

lub bardziej ogólnie

(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))
Jagnięcina
źródło
12
(map vector [1 2 3] [4 5 6])
danlei
źródło
11

aby dać ci dokładnie to, czego chciałeś, mapowanie listmiędzy dwiema listami da ci listę list, jak w twoim przykładzie. Myślę, że wielu Clojurian miałoby tendencję do używania do tego wektorów, chociaż zadziała to ze wszystkim. a wejścia nie muszą być tego samego typu. map tworzy z nich sekwencje, a następnie odwzorowuje sekwencje, więc wszelkie dane wejściowe, które można sekwencjonować, będą działać dobrze.

(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
źródło
1
Myślę, że masz na myśli mapę skrótu i ​​zestaw skrótu zamiast mapy i zestawu.
cgrand
3

Wbudowaną metodą byłaby po prostu funkcja „przeplot”:

(interleave [1 2 3 4] [5 6 7 8]) => [1 5 2 6 3 7 4 8]
lsh
źródło
6
aby osiągnąć cel PO należy dodać(partition 2 (interleave [1 2 3 4][5 6 7 8]))
skuro
tak - Wygląda na to, że nie przyjrzałem się pożądanemu wynikowi PO zbyt dokładnie.
lsh
-1

Istnieje funkcja zwana zipmap, która może mieć podobny efekt (zipmap (1 2 3)(4 5 6)) Wynik jest przepływami: {3 6, 2 5, 1 4}

温 发 琥
źródło
zipmap zwraca mapę, która nie gwarantuje zamówienia
Ilya Shinkarenko
-2

# (zastosuj listę map%) transponuje macierz, tak jak funkcja zip * w Pythonie. Jako definicja makra:

user => (defmacro py-zip [lst] `(zastosuj listę map ~ lst))

# 'user / 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
źródło
Jak to jest lepsze niż zwykłe używanie „mapy”? A jaki jest tu sens makra?
Dave Newton,