2011-11-11 11 views
10

Esiste un modo "corretto" per eseguire un'iterazione su una sequenza bidimensionale in Clojure? Supponiamo che io avevo una lista di elenchi di numeri, come questoiterazione idioomatica su una sequenza dimensionale 2 (o superiore) in Clojure

((1 2 3) 
    (4 5 6) 
    (7 8 9)) 

e volevo generare un nuovo elenco di liste con ogni numero incrementato di uno. C'è un modo semplice per farlo in Clojure senza fare affidamento su mappe annidate o loop/ricorre? Sono stato in grado di farlo, ma le mie soluzioni sono brutte e le trovo difficili da capire quando le rileggo.

Grazie

risposta

13

È sempre possibile utilizzare solo una comprensione di lista. Mi trovo a usarli abbastanza spesso provenienti da uno sfondo imperativo, quindi non so come sia idiomatico. Nel vostro caso specifico, si può fare:

(for [my-list my-matrix] (map inc my-list)) 
+1

ho intenzione di accettare questo, anche se gli altri sono certamente risposte valide Questo mi sembra solo il più breve e più leggibile. – Joel

+4

Si noti che 'for' produce una sequenza lazy, quindi" itera "solo quando viene richiesto il valore. – postfuturist

10

Per il caso bidimensionale, si potrebbe fare qualcosa di simile:

(map #(map inc %) my-two-d-list) 

che non è troppo male a leggere: applicare la funzione #(map inc %) ad ogni elemento in un elenco.

Per il caso di ordine superiore, si sta fondamentalmente parlando di tree-traversal. Vorresti una funzione che includa un albero e una funzione e applica tale funzione a ciascun nodo dell'albero. È possibile trovare le funzioni per questo in clojure.walk.

5

Le altre risposte di Sean e Matt entrambi mostrano modi concisi ed efficaci di ottenere il giusto risultato.

Tuttavia ci sono alcune estensioni importanti si può fare a questo:

  • Sarebbe bello per gestire il caso di dimensioni superiori
  • E 'bene per avvolgere la funzionalità in una funzione di ordine superiore

codice Esempio:

;; general higher order function 
(defn map-dimensions [n f coll] 
    (if (= n 1) 
    (map f coll) 
    (map #(map-dimensions (dec n) f %) coll))) 

;; use partial application to specialise to 2 dimensions 
(def map-2d (partial map-dimensions 2)) 

(map-2d inc 
    '((1 2 3) 
     (4 5 6) 
     (7 8 9))) 
=> ((2 3 4) (5 6 7) (8 9 10)) 
+0

È comunque prezioso vedere come può essere fatto da zero. – Mars

+0

Inoltre, 'prewalk' richiede una funzione che distingua i nodi foglia dai nodi ramo. Non puoi semplicemente dare "inc", per esempio. – Mars

18

Ciò che si descrive è PR ecisely cosa clojure.walk è per:

 
(def matrix [[1 2 3] 
      [4 5 6] 
      [7 8 9]]) 
(use 'clojure.walk :only [prewalk]) 
(prewalk #(if (number? %) (inc %) %) matrix) 
=> [[2 3 4] [5 6 7] [8 9 10]] 

Nota 1: è idiomatica di utilizzare vettori al posto di parentesi per collezioni sequenziali letterali.

Nota 2: tipo di conservazione a piedi.

+0

Grazie per aver menzionato la Nota 1 - Sono ancora un po 'incerto su quando usare l'una o l'altra. – Joel

+0

Questa risposta è indicata dal cheat di Clojure su clojure.org. – John

5

Dall'introduzione di core.matrix nel 2013, questo è ora un modo molto migliore delle operazioni di movimentazione su array multidimensionali:

(use 'clojure.core.matrix) 

(def M [[1 2 3] 
     [4 5 6] 
     [7 8 9]]) 

(emap inc M) 

=> [[2 3 4 ] 
    [5 6 7 ] 
    [8 9 10]] 

Vantaggi di usare core.matrix:

  • pulite, Clojure idiomatica codice
  • Un sacco di funzioni di manipolazione della matrice n-dimensionale per uso generico - transpose, shape, reshape, slice, subarray ecc.
  • Possibilità di collegare implementazioni di array ad alte prestazioni (ad es. per i grandi array numerici)
0

Una risposta tardiva, e forse non esattamente ciò che è necessario: si potrebbe provare flatten. Verrà restituito un ss che si può iterare su:

(flatten '((1 2 3) 
      (4 5 6) 
      (7 8 9))) 

user=> (1 2 3 4 5 6 7 8 9) 

E al fine di incrementare elementi di matrice e rimontare la matrice:

(partition 3 (map inc (flatten '((1 2 3) 
            (4 5 6) 
            (7 8 9))))) 
Problemi correlati