2012-11-05 11 views
7

Al fine di comprendere meglio mapcat ho preso un esempio:mapcat usando la mappa e concat

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

E ha cercato di riprodurre ciò che il documento descrive l'uso, di proposito, mappa e 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. 

In questo modo:

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

Tuttavia, come potete vedere, non funziona. Posso tuttavia usare ridurre come questo, ma non so se è corretto:

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

Le opere di cui sopra, ma non so se è un modo corretto per ricreare, utilizzando mappa e concat, cosa mapcat fa.

Fondamentalmente mi piacerebbe capire di mapcat opere sotto il cofano.

Cosa sta succedendo e come posso accedere all'origine di mapcat? (Sto usando Emacs + nrepl)

+1

Utilizzare 'apply' è probabilmente meglio di' reduce' qui perché 'reduce' farà un' concat' per ogni coppia di argomenti.Dato che 'concat' è pigro, quando i valori vengono effettivamente forzati si potrebbe finire con uno stack di chiamate _really_ deep, che potrebbe causare un overflow dello stack. [Ecco un semplice esempio.] (Https://www.refheap.com/paste/6409) – DaoWen

+2

Solo un suggerimento: nella tua implementazione con 'reduce', non è necessario racchiudere' concat' in una funzione anonima. Funzionerà anche: '(riduci concat (map ...))' ed è preferibile perché gestisce meglio il caso dell'input vuoto. – Alex

risposta

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

La ragione reduce funziona è anche perché è efficace:

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

apply, come utilizzato nel codice sorgente, si espande a:

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

Nel tuo tentativo iniziale con 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) 

È possibile notare che se si chiama concat con un singolo argomento, viene restituito tale argomento.

+1

(applica concat (applica map f colls)) sembra strano. Non riesco a farlo funzionare. * (applica concat (map f colls)) * funziona per me, ma non capisco il doppio uso di * apply *. Ad esempio * (applica concat (applica la mappa # (lista%%) [1 2])) * non funziona !? –

+0

Apply richiede un elenco di parametri in modo che la stringa corretta per il tuo esempio con solo 1 parametro sia (applica concat (applica map # (list%%) [[1 2]]))) – mikkom

2

concat è una funzione variadica in cui può assumere n parametri in cui ogni parametro è una sequenza di valori i.e la firma diventa (defn concat [& lst]]). Mentre nel tuo esempio stai chiamando concat con un singolo argomento supponendo che concat prenda un seq di seq di valori da concatenare e questo è il motivo per cui ottieni il risultato che la stessa lista di liste viene restituita.

(apply concat(apply map #(list % %) [1 2])) non funziona.

(apply map #(list % %) [[1 2]]) o (apply map #(list % %) [1 2] []) funzionerà.

Questo perché l'applicazione si aspetta che l'ultimo parametro sia una sequenza di valori e ogni elemento in tale sequenza di valori verrà passato come parametro alla funzione applicata. In caso di mancata applicazione la chiamata si espanderà a (map #(list % %) 1 2) che è errata e il messaggio di errore mostra anche che non può convertire da lungo a sequenza come sequenze di necessità della mappa come parametri.

Problemi correlati