2013-03-08 11 views
11

Jak rozumiem, spacer i mapa mają zastosowanie do funkcji seq. (spacer pozwala również na zastosowanie funkcji przetwarzania po outer). Jakie są jednak idiomatyczne przypadki używania jednego z drugim?spacer vs mapa do przetwarzania seq

+1

Zastosowanie funkcji seq jest zadanie 'map'. Użyj 'walk', kiedy musisz przejść zarówno przez rekursywnie, jak i przez całą strukturę. –

+0

@ A.Webb możesz opublikować jako odpowiedź. Chciałbym zaznaczyć to jako odpowiedź. również docenią, jeśli możesz wskazać kilka przykładów spaceru. – murtaza52

+1

Czy moje nie odpowiada na twoje pytanie? – sethev

Odpowiedz

6

Stosowanie funkcji do seq jest zadaniem mapy. Skorzystaj z chodzenia, gdy musisz przemierzyć całą i rekursywnie całą strukturę.

Niektóre przykłady walk s można znaleźć pod adresem ClojureDocs, również dostępne w REPL, np. (user/clojuredocs clojure.walk/postwalk). Wiele przykładów jest pedagogicznych i może i powinno być w praktyce wykonywane z map lub for (a czasami reduce).

Typowym przykładem użycia dla modelu walk jest struktura zagnieżdżona, którą chcesz przetwarzać rekursywnie. Niektóre przykłady, w których może to być przydatne, to sama przestrzeń nazw, np. clojure.walk. spójrz na (source clojure.walk/keywordize-keys). [. Uwaga, jeśli chcesz przetwarzać iteracyjnie lub woli, mechanicznych zamki (lub tree-seq dla niektórych prostszych iteracyjnych przypadkach)]

Kolejny przykład, który przychodzi do głowy jest ustne drzewa analizy::

(require '[clojure.walk :as w]) 

(def t [+ [* [- 6 2] [/ 9 3]] [* 2 [+ 7 8]]]) 

(w/postwalk #(if (and (coll? %) (fn? (first %))) (apply (first %) (next %)) %) t) 
;=> 42 

Może przydatna, gdy np zastępując fn? z allowed-fn? itp ocenić wyrażenie , zamiast wywoływać zbyt mocny eval kompilatora:

(eval t) ;=> [#<core$_PLUS_ ... ] 

Ups, formularze są listy, nie VEC Tors:

(def s (w/postwalk #(if (coll? %) (apply list %) %) t)) 
s ;=> (#<core$_PLUS_ ...) 
(eval s) ;=> 42 

Ach, zawiadomienie o kolejnym użycie walk - rekurencyjnie zmianę struktury z zagnieżdżonych wektorów do zagnieżdżonych list.

Iteracyjny przykład medytować:

(require '[clojure.walk :as w]) 

(def s1 (range 8)) 
s1 ;=> (0 1 2 3 4 5 6 7) 
(map inc s1) 
;=> (1 2 3 4 5 6 7 8) 
(w/postwalk #(if (number? %) (inc %) %) s1) 
;=> (1 2 3 4 5 6 7 8) 

(def s2 (partition 2 s1)) 
s2 ;=> ((0 1) (2 3) (4 5) (6 7)) 
(map (partial map inc) s2) 
;=> ((1 2) (3 4) (5 6) (7 8)) 
(w/postwalk #(if (number? %) (inc %) %) s2) 
;=> ((1 2) (3 4) (5 6) (7 8)) 

(def s3 (partition 2 s2)) 
s3 ;=> ((0 1) (2 3) (4 5) (6 7)) 
(map (partial map (partial map inc)) s3) 
;=> (((1 2) (3 4)) ((5 6) (7 8))) 
(w/postwalk #(if (number? %) (inc %) %) s3) 
;=> (((1 2) (3 4)) ((5 6) (7 8))) 

(def s4 (partition 2 s3)) 
s4 ;=> ((((0 1) (2 3)) ((4 5) (6 7)))) 
(map (partial map (partial map (partial map inc))) s4) 
;=> ((((1 2) (3 4)) ((5 6) (7 8)))) 
(w/postwalk #(if (number? %) (inc %) %) s4) 
;=> ((((1 2) (3 4)) ((5 6) (7 8)))) 
10

Semantyka dla map są zasadniczo: zastosować funkcję do każdego elementu w kolekcji i zwraca wyniki leniwie w sekwencji:

(map inC#{0 1 2}) ;outputs (when realized) (1 2 3) 

Należy pamiętać, że wejście było zbiorem ale wyjście jest sekwencją.

Semantyka na spacer są zasadniczo: make zbiór tego samego typu, w których każdy element został zastąpiony przez wartość funkcji inner dla tego elementu, należy zwrócić wynik zastosowania outer do nowej kolekcji:

(walk inc identity #{0 1 2}) ;outputs #{1 2 3} 

Jeśli spojrzysz na kod źródłowy dla innych funkcji w interfejsie API spaceru (http://richhickey.github.com/clojure/clojure.walk-api.html), możesz zobaczyć, jak uczynić spacery również rekurencyjne (lub po prostu użyć tych innych funkcji).

Jeśli chodzi o idiomy, nie jestem pewien. Ale walk jest bardziej złożony, więc prawdopodobnie powinieneś trzymać się map w przypadkach, gdy nie potrzebujesz semantyki, którą oferuje walk.

+0

Myślę, że druga odpowiedź lepiej ilustruje różnicę. Jednak +1 za wysiłek i doceń to! – murtaza52