Pregunta

Aceptar, sin trampas ahora.

No, en serio, tómese un minuto o dos y probar esto.

¿Qué significa "posiciones" hacer?

Editar:. Simplificada de acuerdo con la sugerencia de cgrand

(defn redux [[current next] flag] [(if flag current next) (inc next)])

(defn positions [coll]
  (map first (reductions redux [1 2] (map = coll (rest coll)))))

Ahora bien, ¿qué hay de esta versión?

def positions(coll) {
  def (current, next) = [1, 1]
  def previous = coll[0]
  coll.collect {
    current = (it == previous) ? current : next
    next++
    previous = it
    current
  }
}

Estoy aprendiendo Clojure y yo soy amante de ella, porque siempre he disfrutado de la programación funcional. Me tomó más tiempo para llegar a la solución Clojure, pero me gustó tener que que de una solución elegante. La solución maravillosa está bien, pero estoy en el punto en el que parece que este tipo de programación imperativa aburrido y mecánico. Después de 12 años de Java, me siento en una programación de rutina y funcional con Clojure es el impulso que necesitaba.

Derecho, llegar al punto. Bueno, tengo que ser honesto y decir que me pregunto si voy a entender el código Clojure cuando vuelva a ella meses después. Claro que podría comentar los demonios fuera de ella, pero yo no necesito comentar mi código Java para entenderlo.

Así que mi pregunta es: ¿es una cuestión de conseguir más acostumbrados a los patrones de programación funcional? Se gurús de programación funcional leyendo el código y encontrar que es muy fácil de entender? ¿Qué versión hizo que encontrar más fácil de entender?

Edit: lo que este código hace es calcular las posiciones de los jugadores en función de sus puntos, mientras que llevar la cuenta de los que están vinculados. Por ejemplo:


Pos Points
1. 36
1. 36
1. 36
4. 34
5. 32
5. 32
5. 32
8. 30
¿Fue útil?

Solución

No creo que haya ninguna cosa tal como la legibilidad intrínseca. No es lo que estamos acostumbrados, y lo que no estamos acostumbrados. Yo era capaz de leer las dos versiones de su código OK. Yo podía leer su versión maravillosa con mayor facilidad, a pesar de que no sé maravilloso, porque yo también pasé una década buscando en C y Java y sólo un año mirando Clojure. Eso no dice nada acerca de las lenguas, sólo dice algo sobre mí.

Del mismo modo que puedo leer Inglés con más facilidad que español, pero eso no quiere decir nada sobre la legibilidad intrínseca de esos idiomas tampoco. (Española es en realidad, probablemente, el lenguaje "más legible" de los dos en términos de simplicidad y coherencia, pero todavía no puede leerlo). Estoy aprendiendo japonés en este momento y tener una diablos de un tiempo difícil, pero los hablantes nativos de japonés decir lo mismo de Inglés.

Si usted pasó la mayor parte de su vida la lectura de Java, por supuesto las cosas que se parecen a Java será más fácil de leer que las cosas que no lo hacen. Hasta que haya pasado tanto tiempo buscando en idiomas lispy como buscar en idiomas como C, esto probablemente seguirá siendo así.

Para entender un idioma, entre otras cosas que usted tiene que estar familiarizado con:

  • sintaxis ([vector] vs (list), hyphens-in-names)
  • vocabulario (lo que no significa reductions? ¿Cómo / dónde se puede mirar hacia arriba?)
  • reglas de evaluación (no el tratamiento funciona como funcionan los objetos? Es un error en la mayoría de los idiomas).
  • modismos, como (map first (some set of reductions with extra accumulated values))

Todo esto lleva tiempo y práctica y la repetición para aprender e interiorizar. Pero si pasas los próximos 6 meses leyendo y escribiendo un montón de Clojure, no sólo va a ser capaz de entender que Clojure código de 6 meses a partir de ahora, es probable que lo entienden mejor que lo hace ahora, y tal vez incluso ser capaz de simplificar eso. ¿Qué tal esto:

(use 'clojure.contrib.seq-utils)                                        ;;'
(defn positions [coll]
  (mapcat #(repeat (count %) (inc (ffirst %)))
          (partition-by second (indexed coll))))

En cuanto a Clojure código que escribí hace un año, estoy horrorizado por lo malo que es, pero puedo leerlo bien. (No digo su código Clojure es horrible;. No tenía problemas para leer en absoluto, y yo no soy gurú)

Otros consejos

Estoy de acuerdo con Timoteo: se introduce demasiado abstracciones. Me reelaborado su código y terminó con:

(defn positions [coll]
  (reductions (fn [[_ prev-score :as prev] [_ score :as curr]] 
                (if (= prev-score score) prev curr))
    (map vector (iterate inc 1) coll)))

Sobre su código,

(defn use-prev [[a b]] (= a b))
(defn pairs [coll] (partition 2 1 coll))
(map use-prev (pairs coll))

puede ser simplemente refactorizado como:

(map = coll (rest coll))

editar:. no puede ser más pertinente

El Clojure es complicado para mí. Contiene más abstracciones que necesitan ser entendidos. Este es el precio de la utilización de funciones de orden superior, usted tiene que saber lo que significan. Así, en un caso aislado, imprescindible requiere menos conocimiento. Pero el poder de abstracciones está en sus medios de combinación. Cada bucle imperativo debe ser leído y comprendido, mientras que las abstracciones de secuencia permiten eliminar la complejidad de un bucle y combinar opperations poderosos.

Yo diría, además, que la versión Groovy es al menos parcialmente funcional, ya que utiliza recoger, que es realmente el mapa, una función de orden superior. Tiene algún estado en ella también.

Así es como me gustaría escribir la versión Clojure:

(defn positions2 [coll]
  (let [current (atom 1)
        if-same #(if (= %1 %2) @current (reset! current (inc %3)))]
    (map if-same (cons (first coll) coll) coll (range (count coll)))))

Esto es muy similar a la versión maravillosa, ya que utiliza un mutable "actual", pero difiere en que no tiene una variable siguiente / anterior - en lugar de utilizar secuencias inmutables para aquellos. Como Brian elloquently puso, la lectura no es intrínseco. Esta versión es mi preferencia para este caso en particular, y parece que sentarse en algún lugar en el medio.

El Clojure es más complicado a primera vista; aunque tal vez más elegante. OO es el resultado de hacer un lenguaje más "fácil identificarse" al más alto nivel. Los lenguajes funcionales parece tener un "algorithimc" más (primitiva / primaria) se sienten a la misma. Eso es justo lo que sentí en ese momento. Tal vez esto cambiará cuando tenga más experiencia trabajando con clojure.

Me temo que estamos Decending en el juego de las cuales el lenguaje puede ser la más concisa o resolver un problema en lo más mínimo línea de código.

El problema son 2 pliegues para mí:

  1. ¿Qué tan fácil a primera vista para tener una idea de lo que el código está haciendo ?. Esto es importante para los mantenedores de código.

  2. ¿Es fácil de adivinar la lógica detrás del código ?. Demasiado prolijo / largo aliento ?. Demasiado escueta?

"Hacer todo lo más simple posible, pero no más simple."

Albert Einstein

Yo también estoy aprendiendo Clojure y me encanta. Pero en esta etapa de mi desarrollo, la versión maravilloso era más fácil de entender. Lo que me gusta Clojure es sin embargo la lectura del código y tener la "Aha!" experiencia cuando finalmente "obtener" lo que está pasando. Lo que realmente es disfrutar de la experiencia similar que ocurre unos pocos minutos más tarde, cuando se da cuenta de todas las maneras que el código se podría aplicar a otros tipos de datos sin cambios en el código. He perdido la cuenta del número de veces que he trabajado a través de un código numérico en Clojure y luego, un poco más tarde, el pensamiento de cómo ese mismo código se podría utilizar con cuerdas, los símbolos, los widgets, ...

La analogía que uso es sobre los colores de aprendizaje. Recuerde que cuando usted se detalló el color rojo? Usted entiende bastante rápidamente - hay todas estas cosas de color rojo en el mundo. A continuación, oído el término magenta y se perdieron por un tiempo. Pero, de nuevo, después de un poco más de la exposición, se entiende el concepto y tenía una forma mucho más específica para describir un color en particular. Usted tiene que internalizar el concepto, mantenga un poco más información en su cabeza, pero se termina con algo más poderoso y conciso.

Groovy soporta varios estilos de resolver este problema también:

coll.groupBy{it}.inject([]){ c, n -> c + [c.size() + 1] * n.value.size() }

definitivamente no refactorizado a ser bastante pero no demasiado difícil de entender.

Sé que esto no es una respuesta a la pregunta, pero voy a ser capaz de "comprender" el código mucho mejor si hay pruebas, tales como:

assert positions([1]) == [1]
assert positions([2, 1]) == [1, 2]
assert positions([2, 2, 1]) == [1, 1, 3]
assert positions([3, 2, 1]) == [1, 2, 3]
assert positions([2, 2, 2, 1]) == [1, 1, 1, 4]

Esto me, le dirá dentro de un año, lo que se espera que el código para hacerlo. Mucho mejor que cualquier versión excelente del código que he visto aquí.

Am I realmente fuera de tema?

La otra cosa es que yo creo "legibilidad" depende del contexto. Depende que van a mantener el código. Por ejemplo, con el fin de mantener la versión "funcional" del código Groovy (muy brillante), que se llevará no sólo a los programadores maravilloso, pero los programadores funcionales maravillosos ... El otro, más relevante, el ejemplo es: si unas pocas líneas de código hacen que sea más fácil de entender para los "principiantes" programadores de Clojure, entonces el código general será más fácil de leer, ya que se entiende por una comunidad más amplia: no es necesario haber estudiado clojure durante tres años para ser capaz de comprender el código y realizar modificaciones a la misma.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top