2012-11-05 18 views
7

W celu lepszego zrozumienia mapcat Wziąłem przykład:mapcat używając mapy i concat

user> (mapcat #(list % %) [1 2 3]) 
(1 1 2 2 3 3) 

i starał się odtworzyć to, co doc opisuje stosując na celu mapę i concat:

user> (doc mapcat) 
clojure.core/mapcat 
([f & colls]) 
    Returns the result of applying concat to the result of applying map 
    to f and colls. Thus function f should return a collection. 

Robiąc to:

user> (concat (map #(list % %) [1 2 3])) 
((1 1) (2 2) (3 3)) 

Jednak jak widać, nie działa. Mogę jednak używać zmniejszyć tak, ale nie wiem, czy to jest poprawne:

user> (reduce #(concat %1 %2) (map #(vec (list % %)) [1 2 3])) 
(1 1 2 2 3 3) 

Powyższe działa, ale nie wiem, czy jest to prawidłowy sposób odtworzyć, używając mapę i concat, co mapcat robi.

Zasadniczo chciałbym zrozumieć mapcat działa pod maską.

Co się dzieje i jak mogę uzyskać dostęp do źródła mapcat? (Używam Emacsa + nrepl)

+1

Użycie 'apply' jest prawdopodobnie lepsze niż' reduce', ponieważ 'reduce' spowoduje' concat' dla każdej pary argumentów.Ponieważ 'concat' jest leniwy, kiedy wartości są faktycznie wymuszane, możesz skończyć z stosem głębo- kich połączeń, prawdopodobnie powodując przepełnienie stosu. [Oto prosty przykład.] (Https://www.refheap.com/paste/6409) – DaoWen

+2

Tylko wskazówka - w swojej implementacji z 'reduce', nie ma potrzeby owijać' concat' w anonimową funkcję. Będzie to również działać: '(redu concat (map ...))' i jest lepsze, ponieważ lepiej obsługuje przypadek pustych danych wejściowych. – Alex

Odpowiedz

6
user=> (source mapcat) 
(defn mapcat 
    "Returns the result of applying concat to the result of applying map 
    to f and colls. Thus function f should return a collection." 
    {:added "1.0"} 
    [f & colls] 
    (apply concat (apply map f colls))) 
nil 
user=> 

Powodem reduce działa również dlatego, że jest skutecznie:

(concat (concat '(1 1) '(2 2)) '(3 3)) 

apply, stosowany w kodzie źródłowym, rozszerza się:

(concat '(1 1) '(2 2) '(3 3)) 

Podczas pierwszej próby z concat:

user=> (map #(list % %) [1 2 3]) 
    ((1 1) (2 2) (3 3)) 
    user=> (concat (list '(1 1) '(2 2) '(3 3))) 
    ((1 1) (2 2) (3 3)) 
    user=> (concat [1]) 
    (1) 

Możesz zobaczyć, że jeśli wywołasz concat z pojedynczym argumentem, zwróci ten argument.

+1

(zastosować concat (zastosować mapę f colls)) wygląda dziwnie. Nie mogę zmusić go do działania. * (zastosowanie concat (map f colls)) * działa dla mnie, ale nie rozumiem podwójnego zastosowania * apply *. Na przykład * (zastosuj konkat (zastosuj mapę # (lista%%) [1 2])) * nie działa !? –

+0

Zastosuj chce listę parametrów, więc poprawny fort dla twojego przykładu z tylko jednym parametrem będzie (zastosuj konkat (zastosuj mapę # (lista%%) [[1 2]])) – mikkom

2

jest funkcją wariantową, to znaczy może przyjmować n parametrów, gdzie każdy parametr jest sekwencją wartości, to znaczy, że podpis staje się (defn concat [& lst]]). Podczas gdy w twoim przykładzie wywołujesz konkat z pojedynczym argumentem zakładającym, że konkat ma zająć co najmniej jedną sekwę wartości, i dlatego otrzymujesz wynik, tj. Ta sama lista list jest zwracana.

(apply concat(apply map #(list % %) [1 2])) nie będzie działać.

(apply map #(list % %) [[1 2]]) lub (apply map #(list % %) [1 2] []) będzie działać.

Jest tak, ponieważ aplikacja apply oczekuje, że ostatni parametr będzie sekwencją wartości, a każdy element w tej sekwencji wartości zostanie przekazany jako parametr do zastosowanej funkcji. W przypadku niepowodzenia zgłoszenia, połączenie rozwinie się do (map #(list % %) 1 2), co jest błędne, a komunikat o błędzie pokazuje również, że nie można go przekształcić z długiej na sekwencję jako sekwencji sekwencji map jako parametrów.