2012-11-05 7 views
7

Um eine bessere mapcat zu verstehen, habe ich ein Beispiel:mapcat mit Karte und concat

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

und versuchte zu reproduzieren, was der Doc mit beschreibt, absichtlich, Karte und 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. 

Damit verfolgt:

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

Wie Sie jedoch sehen können, funktioniert es nicht. Ich kann jedoch wie diese reduzieren verwenden, wissen aber nicht, ob es richtig ist:

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

Die oben genannten Arbeiten, aber ich weiß nicht, ob es eine richtige Art und Weise ist neu zu erstellen, mit Karte und concat, was mapcat tut.

Grundsätzlich würde ich gerne von mapcat funktioniert unter der Haube.

Was passiert und wie kann ich auf die Quelle mapcat zugreifen? (Ich verwende Emacs + nrepl)

+1

Die Verwendung von 'apply 'ist wahrscheinlich besser als' Reduzieren 'hier, weil' reduce 'ein' concat' für jedes Argumentpaar ausführt.Da "concat" faul ist, wenn die Werte tatsächlich erzwungen werden, könnten Sie mit einem _really_ deep-Aufruf-Stack enden, was möglicherweise zu einem Stack-Überlauf führt. [Hier ist ein einfaches Beispiel.] (Https://www.refheap.com/paste/6409) – DaoWen

+2

Nur ein Tipp - in Ihrer Implementierung mit "reduzieren", ist es nicht notwendig, "concat" in einer anonymen Funktion zu wickeln. Dies funktioniert auch: '(reduziere concat (map ...))' und ist vorzuziehen, da es den Fall einer leeren Eingabe besser behandelt. – Alex

Antwort

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=> 

Der Grund reduce funktioniert auch, weil es effektiv ist:

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

apply, wie im Quellcode verwendet, erweitert um:

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

In Ihrem ersten Versuch mit 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) 

Sie können sehen, dass wenn Sie concat mit einem einzigen Argument aufrufen, es dieses Argument zurückgibt.

+1

(concat anwenden (Karte für Kollis anwenden)) sieht komisch aus. Ich kann es nicht zur Arbeit bringen. * (concat anwenden (map f colls)) * funktioniert bei mir, aber ich verstehe die doppelte Verwendung von * apply * nicht. Zum Beispiel * (apply concat (apply map # (list%%) [1 2])) * funktioniert nicht !? –

+0

Übernehmen möchte eine Liste von Parametern, so dass das richtige Fortm für Ihr Beispiel mit nur 1 Parameter wäre (concate (apply map # (list%%) [[1 2]])) – mikkom

2

concat ist eine variadische Funktion, d. H. Sie kann n Parameter annehmen, wobei jeder Parameter eine Folge von Werten ist, d. H. Die Signatur wird (defn concat [& lst]]). Während Sie in Ihrem Beispiel concat mit einem einzigen Argument aufrufen, vorausgesetzt, concat nimmt eine Seq von Seq von Werten auf, die verkettet werden sollen, und deshalb erhalten Sie das Ergebnis, d. H. Die gleiche Liste der Liste wird zurückgegeben.

(apply concat(apply map #(list % %) [1 2])) wird nicht funktionieren.

(apply map #(list % %) [[1 2]]) oder (apply map #(list % %) [1 2] []) wird funktionieren.

Dies liegt daran, dass apply erwartet, dass der letzte Parameter eine Folge von Werten ist und jedes Element in dieser Sequenz von Werten als Parameter an die angewendete Funktion übergeben wird. In dem Fall, dass der Antrag nicht erfolgreich war, wird der Anruf auf (map #(list % %) 1 2) erweitert, was falsch ist, und die Fehlermeldung zeigt auch, dass er nicht lange als Sequenz in Sequenzsequenzen als Parameter konvertieren kann.