Question

OK, pas de triche maintenant.

Non, vraiment, prenez une minute ou deux et essayer cela.

Qu'est-ce que "les positions" faire?

Edit:. Simplifiée selon la suggestion 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)))))

Maintenant, que diriez-vous cette version?

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

J'apprends Clojure et je l'aimer, parce que je l'ai toujours aimé la programmation fonctionnelle. Il m'a fallu plus de temps pour arriver à la solution Clojure, mais j'apprécié d'avoir à pense d'une solution élégante. La solution Groovy est bien, mais je suis au point où je trouve ce type de programmation impératif ennuyeux et mécanique. Après 12 ans de Java, je me sens dans une ornière et la programmation fonctionnelle avec Clojure est le coup de pouce que je avais besoin.

Droit, arriver au point. Eh bien, je dois être honnête et dire que je me demande si je comprendrai le code Clojure quand je vais revenir mois plus tard. Bien sûr, je pourrais commenter le diable hors de lui, mais je ne ai pas besoin de commenter mon code Java pour le comprendre.

Ma question est: est-il une question d'obtenir plus utilisé pour les modèles de programmation fonctionnelle? Sont les gourous de la programmation fonctionnelle lecture de ce code et trouver un jeu d'enfant à comprendre? Quelle version a fait vous trouver plus facile à comprendre?

Edit: ce que ce code est de calculer les positions des joueurs en fonction de leurs points, tout en garder la trace de ceux qui sont liés. Par exemple:


Pos Points
1. 36
1. 36
1. 36
4. 34
5. 32
5. 32
5. 32
8. 30
Était-ce utile?

La solution

Je ne pense pas qu'il y ait une telle chose que la lisibilité intrinsèque. Il y a ce que vous êtes habitué, et ce que vous ne sont pas habitués. J'ai pu lire les deux versions de votre code OK. Je pouvais lire votre version Groovy plus facilement, même si je ne sais pas Groovy, parce que moi aussi passé une décennie regarder C et Java et seulement un an regardant Clojure. Cela ne dit rien au sujet des langues, il est dit que quelque chose sur moi.

De même, je peux lire l'anglais plus facilement que l'espagnol, mais cela ne dit rien sur la lisibilité intrinsèque de ces langues soit. (L'espagnol est en fait probablement le « plus lisible » la langue des deux en termes de simplicité et de cohérence, mais je ne peux toujours pas lire). J'apprends droit japonais maintenant et d'avoir un diable d'un moment difficile, mais les locuteurs japonais natifs dire la même chose en anglais.

Si vous avez passé la majeure partie de votre vie à lire Java, des choses de cours qui ressemblent à Java sera plus facile à lire que des choses qui ne le font pas. Jusqu'à ce que vous avez passé autant de temps à regarder des langues Lispy en regardant langues C-like, cela restera probablement vrai.

Pour comprendre une langue, entre autres choses que vous devez être au courant:

  • syntaxe ([vector] vs. (list), hyphens-in-names)
  • vocabulaire (ce qui ne signifie reductions? Comment / où peut-on le chercher?)
  • règles d'évaluation (le traitement ne fonctionne comme le travail des objets? Il est une erreur dans la plupart des langues.)
  • idiomes, comme (map first (some set of reductions with extra accumulated values))

Tous ces prennent du temps et de la pratique et la répétition d'apprendre et d'intérioriser. Mais si vous passez les 6 prochains mois lire et à écrire beaucoup de Clojure, non seulement vous serez en mesure de comprendre que le code Clojure 6 mois à partir de maintenant, vous comprendrez probablement mieux que vous faites maintenant, et peut-être même être en mesure de simplifier il. Que diriez-vous ceci:

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

En regardant le code Clojure je l'ai écrit il y a un an, je suis horrifié à quel point il est, mais je peux le lire sur OK. (Ne dis pas votre code Clojure est horrible. Je n'avais pas du mal à lire du tout, et je ne suis pas un gourou)

Autres conseils

Je suis d'accord avec Timothée: vous introduisez des abstractions trop. J'ai retravaillé votre code et fini avec:

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

A propos de votre code,

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

peut être tout simplement comme refondus:

(map = coll (rest coll))

modifier. ne peut pas être plus pertinent

L'un Clojure est alambiqué pour moi. Il contient plus de abstractions qui doivent être compris. Ceci est le prix de l'utilisation des fonctions d'ordre supérieur, vous devez savoir ce qu'ils veulent dire. Ainsi, dans un cas isolé, il faut impérativement moins de connaissances. Mais la puissance des abstractions est dans leurs moyens de combinaison. Chaque boucle doit impérativement être lu et compris, alors que des abstractions séquence vous permettent de supprimer la complexité d'une boucle et de combiner opperations puissants.

Je dirais que plus la version Groovy est au moins partiellement fonctionnel car il utilise la collecte, qui est vraiment la carte, une fonction d'ordre supérieur. Il a un certain état aussi.

Voici comment j'écrire la version 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)))))

Ceci est tout à fait similaire à la version Groovy en ce qu'il utilise un mutable « courant », mais diffère en ce qu'elle n'a pas de variable suivante / prev - au lieu d'utiliser des séquences immuables pour ceux-ci. Comme Brian elloquently dit, la lisibilité est pas intrinsèque. Cette version est ma préférence pour ce cas particulier, et semble asseoir quelque part au milieu.

L'une Clojure est plus compliqué à première vue; mais il peut-être plus élégant. OO est le résultat de rendre la langue plus « racontable » au niveau supérieur. Les langages fonctionnels semble avoir un plus « algorithimc » (primitive / élémentaire) se sentent à elle. C'est juste ce que je ressentais à ce moment. Peut-être que cela va changer quand j'ai plus d'expérience de travail avec Clojure.

Je crains que nous decending dans le jeu dont la langue peut être le plus concis ou résoudre un problème dans la moindre ligne de code.

La question sont 2 plis pour moi:

  1. Comment facile à première vue d'obtenir une idée de ce que fait le code ?. Ceci est important pour les mainteneurs de code.

  2. Comment est-il facile à deviner la logique derrière le code ?. Trop bavard / longue haleine ?. Trop laconique?

"Faire tout aussi simple que possible, mais pas plus simple."

Albert Einstein

Je suis trop apprendre Clojure et l'aimer. Mais à ce stade de mon développement, la version Groovy était plus facile à comprendre. Ce que j'aime Clojure est bien la lecture du code et ayant le « Aha! » expérience lorsque vous enfin « obtenir » ce qui se passe. Ce que je vraiment profiter de l'expérience est similaire qui se produit quelques minutes plus tard, lorsque vous vous rendez compte de toutes les façons dont le code pourrait être appliqué à d'autres types de données sans modification du code. J'ai perdu le compte du nombre de fois où j'ai travaillé par un code numérique dans Clojure puis, un peu plus tard, la pensée de la façon dont ce code pourrait même être utilisé avec des chaînes, des symboles, des widgets, ...

L'analogie que je l'utilise est sur les couleurs d'apprentissage. Rappelez-vous quand vous avez été présenté à la couleur rouge? Vous l'avez compris assez rapidement - il y a tout ce genre de choses rouge dans le monde. Ensuite, vous avez entendu le magenta terme et ont été perdu pendant un certain temps. Mais encore une fois, vous compris après un peu plus d'exposition le concept et avait une façon de décrire une couleur particulière beaucoup plus spécifique. Vous devez internaliser le concept, tenir un peu plus d'informations dans votre tête, mais vous vous retrouvez avec quelque chose de plus puissant et concis.

Groovy supporte différents styles de résoudre ce problème aussi:

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

certainement pas être assez remaniée avec, mais pas trop difficile à comprendre.

Je sais que ce n'est pas une réponse à la question, mais je vais pouvoir « comprendre » le code beaucoup mieux s'il y a des tests, tels que:

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]

Cela me dire, dans un an, ce que le code est censé faire. Beaucoup mieux que toute excellente version du code que je l'ai vu ici.

Suis-je vraiment hors sujet?

L'autre chose est, je pense « lisibilité » dépend du contexte. Cela dépend de qui maintiendra le code. Par exemple, afin de maintenir la version « fonctionnelle » du code Groovy (mais brillant), il faudra non seulement les programmeurs Groovy, mais les programmeurs Groovy fonctionnels ... L'autre, plus pertinent, par exemple est: si quelques lignes de code rendent plus facile à comprendre pour « débutants » programmeurs Clojure, le code sera plus lisible globale, car il sera compris par une plus grande communauté: pas besoin d'avoir étudié Clojure pendant trois ans pour être en mesure de saisir le code et apporter des modifications à elle.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top