2011-11-17 18 views
8

Soy nuevo en Clojure, y he estado traduciendo algunos trabajos de manipulación de datos que hice recientemente como una ayuda para el aprendizaje. Tengo una traducción de función que funciona bien, y es más corta, pero se siente mucho menos legible. ¿Alguien puede sugerir una forma más legible y/o más idiomática para manejar esto?¿Cuál es la mejor forma de manejar esta transformación de secuencia en Clojure?

En 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 

Mi intento de traducción 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)))))) 

Podría ser que es menos legible sólo por mi inexperiencia con Clojure, pero en particular, el truco de anteponer un elemento al vector de entrada me parece que oscurece la intención. Todas las soluciones que probé y que no usaron el truco precedente fueron mucho más largas y feas.

Muchas transformaciones de secuencia son increíblemente elegantes en Clojure, pero las que encuentro desafiantes hasta ahora son las siguientes, que a) se prestan a la manipulación por índice en lugar de por elemento, y/o b) requieren un manejo especial para ciertos elementos.

Gracias por cualquier sugerencia.

Respuesta

12

Idiomatic Clojure tiende a manipular las secuencias como un todo, en lugar de elementos individuales. Se podría definir create-diff-vector en Inglés como:

El resultado está consistiendo un vector de:

  • un cero o el primer elemento de la entrada, dependiendo de si start-zero es verdadera o falsa, respectivamente; seguido de
  • diferencias entre la secuencia de entrada sin el primer elemento y la secuencia de entrada sin el último elemento.

La segunda parte se puede ilustrar de esta manera: para la entrada (31 41 59 26 53), tenemos

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

Lo cual, traducido a Clojure, se vuelve muy concisa:

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

Algunos puntos a tener en cuenta :

  • Un signo de interrogación a finales de start-zero? sirve como una pista de que aquí se espera un booleano.
  • El código explota el hecho de que map haciendo ping a una función sobre secuencias de diferentes longitudes termina al final de la secuencia más corta.
+2

Muy bueno.Siempre me olvido de que puedo hacer esto '(map foo x (rest x))' - Por lo general, busco 'partition' en su lugar, con algo como '(para [[ab] (partición 2 1 v)] (- ba)) '. El mapa es mucho más agradable, gracias por el recordatorio. – amalloy

+0

Gracias. Esa es una explicación realmente clara, y una forma * mucho * más elegante de expresarlo. Definitivamente me está llevando algo de tiempo entender lo que piensa Clojure, pero ya estoy viendo recompensas. – eggsyntax

+0

Heh. Cinco años más tarde, ahora un desarrollador profesional de Clojure/script, me topé con esto de nuevo. Escribió una solución de forma independiente, y terminó siendo palabra por palabra idéntica a la tuya, @daniel. Gracias por ayudarme a seguir el camino correcto en aquel entonces :) – eggsyntax

1

Esta aplicación sería más idiomática:

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

primera vez Prepend ya sea el primer valor del vector o 0 para el vector de entrada. Luego uso map para restar el vector de sí mismo, desplazado por una posición.

Cuestiones relacionadas