2011-11-17 8 views
8

Sono nuovo di Clojure e ho tradotto alcuni lavori di manipolazione dei dati che ho svolto di recente come aiuto per l'apprendimento. Ho una traduzione di funzioni che funziona bene, ed è più breve, ma si sente molto meno leggibile. Qualcuno può suggerire un modo più leggibile e/o più idiomatico per gestirlo?Qual è il modo migliore per gestire questa trasformazione di sequenza in Clojure?

In Python:

def createDifferenceVector(v,startWithZero=True): 
    deltas = [] 
    for i in range(len(v)): 
     if i == 0: 
      if startWithZero: 
       deltas.append(0.0) 
      else: 
       deltas.append(v[0]) 
     else: 
      deltas.append(v[i] - v[i-1]) 
    return deltas 

Il mio tentativo di traduzione Clojure:

(defn create-diff-vector [v start-zero] 
    (let [ext-v (if start-zero 
       (cons (first v) v) 
       (cons 0 v))] 
    (for [i (range 1 (count ext-v))] 
     (- (nth ext-v i) (nth ext-v (- i 1)))))) 

Potrebbe essere che è meno leggibile solo a causa della mia inesperienza con Clojure, ma in particolare, il trucco di anteporre un elemento al vettore di input mi sembra che offuschi l'intenzione. Tutte le soluzioni che ho provato e che non hanno utilizzato il trucco in preparazione sono state molto più lunghe e brutte.

Molte trasformazioni della sequenza sono incredibilmente eleganti in Clojure, ma quelli che trovo difficile finora sono quelli come questo, che a) si prestano a manipolazioni da parte indice piuttosto che per elemento, e/o b) richiedono una gestione speciale per alcuni elementi.

Grazie per eventuali suggerimenti.

risposta

12

Il Clojure idiomatico tende a manipolare le sequenze nel loro complesso, piuttosto che i singoli elementi. Si potrebbe definire create-diff-vector in inglese:

Il risultato è un vettore costituito da:

  • uno zero o il primo elemento di ingresso, a seconda se start-zero è vero o falso, rispettivamente; seguito da
  • differenze tra la sequenza di input senza il primo elemento e la sequenza di input senza l'ultimo elemento.

La seconda parte può essere illustrato nel seguente modo: per l'ingresso (31 41 59 26 53), abbiamo

 
    input without the first element: (41 59 26 53) 
- input without the last element: (31 41 59 26) 
=================================================== 
    result:       (10 18 -33 27) 

Quale tradotto in Clojure, diventa notevolmente concisa:

(defn diff-vector [v start-zero?] 
    (into [(if start-zero? 0 (first v))] 
    (map - (rest v) v)))) 

Alcuni punti da notare :

  • Un punto interrogativo alla fine di start-zero? serve come suggerimento che qui è previsto un booleano.
  • Il codice sfrutta il fatto che map esegue il ping di una funzione su sequenze di lunghezze diverse e termina alla fine della sequenza più breve.
+2

Molto interessante.Dimentico sempre che posso fare ciò '(map pippo x (riposa x))' cosa - di solito raggiungo 'partition' invece, con qualcosa come' (per [[ab] (partizione 2 1 v)] (- ba)) '. La mappa è molto più bella, grazie per il promemoria. – amalloy

+0

Grazie. Questa è una spiegazione molto chiara e un modo * molto * più elegante di esprimerlo. Mi sta sicuramente prendendo un po 'di tempo per avvolgere la mia mente sul modo di pensare Clojure, ma sto già vedendo dei profitti. – eggsyntax

+0

Heh. Cinque anni dopo, ora uno sviluppatore di Clojure/script professionale, mi è capitato di imbattermi di nuovo in questo. Ha scritto una soluzione in modo indipendente, e ha finito per essere quasi parola per parola identica alla tua, @ Daniel. Grazie per avermi aiutato a prendere la strada giusta da quella parte :) – eggsyntax

1

Questa implementazione sarebbe più idiomatica:

(defn create-diff-vector [v start-with-zero?] 
    (let [v (cons (if start-with-zero? (first v) 0) v)] 
     (map - (rest v) v))) 

ho Prepend sia il primo valore del vettore o 0 per il vettore di ingresso. Quindi uso map per sottrarre il vettore da sé, spostato di una posizione.

Problemi correlati