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?
zip
bibliotece Tupelo jest funkcja: cloojure.github.io/doc/tupelo/tupelo.core.html#var-zipOdpowiedzi:
(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. (Thezip
,zip2
,zip3
, ... rodzina może być traktowane jako specjalizacjizipWith
rodziny do wspólnej przypadku użycia z tupling).W przeciwieństwie do tego Clojure i inne Lispy mają dobre wsparcie dla funkcji zmiennej arity;
map
jest jednym z nich i może być używany do „podkręcania” w sposób podobny do HaskellazipWith (\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.)
źródło
zip
wtedy, gdy kolekcje nie są tej samej długości. Ruby będzie kontynuować przetwarzanie i dostarczaćnil
pliki do krótszej kolekcji, podczas gdy Clojure przestanie przetwarzać, gdy jedna z kolekcji zostanie wyczerpana.zip
zachowuje sięmap
pod tym względem jak Clojure's .(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))
źródło
(map vector [1 2 3] [4 5 6])
źródło
aby dać ci dokładnie to, czego chciałeś, mapowanie
list
mię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))
źródło
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]
źródło
(partition 2 (interleave [1 2 3 4][5 6 7 8]))
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
# (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))
źródło