2010-10-29 9 views
16

Dato una raccolta, voglio ripetere tutte le coppie in una raccolta. EsempioModo idiomatico per scorrere tutte le coppie di una raccolta in Clojure

(all-pairs seq) 

(all-pairs '(a b c d)) => ([a b] [a c] [a d] [b c] [b d] [c d])) 

Qui è la mia idea

(defn all-pairs [coll] 
    (for [ [idx elmt] (indexed coll) 
     other-elmt (subvec coll (inc idx))] 
    (vector elmt other-elm))) 

Ma non si sente idiomatica

risposta

17

ne dite:

(use 'clojure.contrib.combinatorics) 
(vec (map vec (combinations '(a b c d) 2))) 
+0

scusa, non riconosciuto, che il contenitore esterno è una lista. Quindi la versione corretta è (map vec (combinazioni '(a b c d) 2)) – Thomas

+0

+1 per usare effettivamente ciò che è disponibile. –

4

Posso suggerire:

(defn all-pairs [sq] (for [i sq j sq] [i j])) 

EDIT: Chiaramente ho letto male la questione; dal momento che desideri solo coppie distinte non duplicate, possiamo ancora utilizzare questo approccio se esiste un ordinamento naturale su qualunque dominio tu stia chiamando questa funzione.

(defn all-pairs [sq] (filter #(< (first %) (second %)) (for [i sq j sq] [i j]))) 

EDIT 2

anche:

(defn all-pairs [sq] 
    (partition 2 (flatten (map (fn [sqi] (map #(vector %1 %2) sq sqi)) 
        (take-while not-empty (iterate rest (rest sq))))))) 
+0

Un problema è che questo include termini come [b a] e [a a], che dalla descrizione non devono essere inclusi. –

+0

Sì, questa era la mia idea iniziale, ma include [b a] e [a b]. Ne voglio solo uno. – Frank

+0

Mi piace la soluzione per usare quando filtrare i duplicati: '(per [un col, b col,: quando (> (int a) (int b))] [ab])' –

6

pigro, e relativamente veloce.

(defn all-pairs [coll] 
    (when-let [s (next coll)] 
    (lazy-cat (for [y s] [(first coll) y]) 
       (all-pairs s)))) 

(defn all-pairs [coll] 
    (let [x (first coll) xs (next coll)] 
    (when xs 
     (lazy-cat 
     (map (fn [y] [x y]) xs) 
     (all-pairs xs))))) 

(all-pairs [1 2 3 4]) ;; => ([1 2] [1 3] [1 4] [2 3] [2 4] [3 4])

(all-pairs '(a b c d)) ;; => ([a b] [a c] [a d] [b c] [b d] [c d])

0

Una semplice versione ricorsiva che dovrebbe fare quello che vuoi:

(defn all-pairs [coll] 
    (let [x (first coll) 
     xs (rest coll)] 
    (if (empty? xs) 
     nil 
     (concat 
     (map (fn [y] [x y]) xs) 
     (all-pairs xs))))) 
+0

Penso che tu voglia fare la tua chiamata ricorsiva con (recur xs) piuttosto che (all-pairs xs). –

+0

Beh, non deve essere, ma lo stack esploderà. –

+0

Ripulito un po ': https://gist.github.com/ae0d9ebf85e9ba6e2cb3 – MayDaniel

0

Non la soluzione più veloce, ma:

; handy helper function 
(defn tails [v] 
    "Given a sequence (a b c), returns all tails: (a b c) (b c) (c)" 
    (when (seq v) 
    (lazy-cat (list v) (tails (rest v))))) 

(defn pair* [v] 
    "Match the first item in the list with all others in pairs." 
    (when (> (count v) 1) 
    (for [y v] [(first v) y]))) 

(defn all-pairs [v] 
    (apply concat (map pair* (tails v)))) 
6
(defn all-pairs [coll] 
    (loop [[x & xs] coll 
     result []] 
    (if (nil? xs) 
     result 
     (recur xs (concat result (map #(vector x %) xs)))))) 
+0

+1: questa risposta è la mia preferita. –

+0

Se le prestazioni sono un problema, la sostituzione di 'concat' con' apply conj' accelera notevolmente. –

+0

fantastico! stavo cercando un idioma per ... ognuno, destrutturando il parametro x nel loop è una grande idea! grazie –

0

ne dici di questo?

(defn all-pairs [coll] 
(when coll 
    (concat (map vector (repeat (first coll)) (rest coll)) 
      (all-pairs (next coll))))) 

Oppure, se si cerca un ss pigro:

(defn all-pairs [coll] 
(lazy-seq 
    (when coll 
    (concat (map vector (repeat (first coll)) (rest coll)) 
      (all-pairs (next coll)))))) 
0

Che dire di questo?

(defn seq->pairs 
    [s] 
    (loop [res [] s s] 
    (let [[head next] (split-at 2 s) 
      res (conj res head)] 
     (if (empty? next) res (recur res next))))) 
0

Solo un altro possibile soluzione:

(defn all-pairs 
     [c] 
     (mapcat #(drop % %2) 
       (range 1 (count c)) 
       (partition (count c) (for [a c b c] [a b])))) 


(all-pairs '(a b c d)) => ([a b] [a c] [a d] [b c] [b d] [c d])) 
(all-pairs [5 4 3 2 1]) => ([5 4] [5 3] [5 2] [5 1] [4 3] [4 2] [4 1] [3 2] [3 1] [2 1]) 
(all-pairs "pairs") => ([\p \a] [\p \i] [\p \r] [\p \s] [\a \i] [\a \r] [\a \s] [\i \r] [\i \s] [\r \s]) 
1

Se si vuole scrivere il proprio combinations funzione in "stile accademico," si può provare

(defn comb [xs m] 
    (cond 
    (= m 0) (list()) 
    (empty? (seq xs))() 
    :else (let [x (first xs) 
       xs (rest xs)] 
      (concat 
      (map #(cons x %) (comb xs (- m 1))) 
      (comb xs m))))) 

e poi applicarlo alla vostra problema come segue

(map vec (comb '(a b c d) 2)) 
([a b] [a c] [a d] [b c] [b d] [c d]) 
+0

Ho appena realizzato (poiché StackOverflow ha riorganizzato le risposte del candidato sulla pagina) questa è una minima generalizzazione della soluzione di @ mikera di seguito. –

Problemi correlati