Pergunta

OK, não traindo agora.

Não, realmente, levar um minuto ou dois e tentar fazer isso.

O que "posições" fazer?

Edit:. Simplificada de acordo com a sugestão 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)))))

Agora, como sobre esta versão?

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

Estou aprendendo Clojure e eu estou amando isso, porque eu sempre gostei de programação funcional. Levei mais tempo para chegar a uma solução Clojure, mas eu gostava de ter que pense de uma solução elegante. A solução Groovy está bem, mas eu estou no ponto onde eu encontrar este tipo de programação imperativa chato e mecânica. Após 12 anos de Java, eu me sinto em um barranco e funcional programação com Clojure é o impulso que eu precisava.

Right, chegar ao ponto. Bem, eu tenho que ser honesto e dizer que eu me pergunto se eu vou entender o código Clojure quando eu voltar a ele meses depois. Claro que eu poderia comentar o Parreira fora dele, mas eu não preciso de comentar meu código Java para compreendê-lo.

Então, minha pergunta é: é uma questão de ser mais usado para padrões de programação funcional? São gurus de programação funcional ler este código e encontrar uma brisa para entender? Qual versão fez você encontrar mais fácil de entender?

Editar: o que este código faz é calcular as posições dos jogadores de acordo com os seus pontos, enquanto faixa de manter aqueles que estão vinculados. Por exemplo:


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

Solução

Eu não acho que há qualquer coisa como legibilidade intrínseco. Não é o que você está acostumado, eo que você não está acostumado. Eu era capaz de ler as duas versões do seu código OK. Eu poderia realmente ler sua versão Groovy mais facilmente, embora eu não sei Groovy, porque eu também passou uma década olhando para C e Java e apenas um ano olhando para Clojure. Isso não diz nada sobre as línguas, ele só diz algo sobre mim.

Da mesma forma que eu posso ler em Inglês mais facilmente do que o espanhol, mas isso não diz nada sobre a legibilidade intrínseca dessas línguas também. (Espanhol é, na verdade, provavelmente, a linguagem "mais legível" dos dois em termos de simplicidade e coerência, mas eu ainda não pode lê-lo). Estou aprendendo a direita japonesa agora e ter um pedaço de um tempo difícil, mas falantes japoneses nativos dizer o mesmo sobre Inglês.

Se você passou a maior parte de sua vida lendo Java, obviamente coisas que se parecem com Java será mais fácil de ler do que as coisas que não. Até que você tenha passado tanto tempo olhando para línguas lispy como olhar para C-como línguas, isto irá provavelmente permanecer fiel.

Para entender uma língua, entre outras coisas que você tem que estar familiarizado com:

  • sintaxe ([vector] vs. (list), hyphens-in-names)
  • vocabulário (o que faz reductions média? Como / onde você pode procurá-lo?)
  • regras de avaliação (não funciona como objetos trabalho tratamento? É um erro na maioria dos idiomas.)
  • idiomas, como (map first (some set of reductions with extra accumulated values))

Todos estes levam tempo e prática e repetição para aprender e internalizar. Mas se você passar os próximos 6 meses lendo e escrevendo lotes de Clojure, não só vai ser capaz de entender que o código Clojure 6 meses a partir de agora, você provavelmente vai entender isso melhor do que você faz agora, e talvez até mesmo ser capaz de simplificar isto. Como sobre isto:

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

Olhando para o código Clojure Eu escrevi um ano atrás, eu estou horrorizada com o quão ruim é, mas posso lê-lo OK. (Não estou dizendo que seu código Clojure é horrível;. Eu não tinha problemas de lê-lo em tudo, e eu não sou nenhum guru)

Outras dicas

Eu concordo com Timothy: você introduzir demais abstrações. Eu reescrevi o código e terminou com:

(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 o seu código,

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

pode ser simplesmente reformulado como:

(map = coll (rest coll))

edit:. pode não ser mais relevantes

A única Clojure é complicado para mim. Ele contém mais abstrações que precisam ser entendidas. Este é o preço do uso de funções de ordem superior, você tem que saber o que eles significam. Assim, em um caso isolado, imperativo exige menos conhecimento. Mas o poder de abstrações está em seus meios de combinação. Cada ciclo imperativo deve ser lido e compreendido, enquanto abstrações seqüência permitir que você remover a complexidade de um loop e combinar opperations poderosos.

Gostaria ainda argumentam que a versão Groovy é pelo menos parcialmente funcional como ele usa a cobrar, que é realmente mapear, uma função de ordem superior. Tem algum estado nele também.

Aqui está como eu iria escrever a versão 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)))))

Este é bastante semelhante à versão Groovy em que ele usa um mutáveis ??"correntes", mas difere na medida em que não tem uma próxima variável / prev - em vez de usar as sequências imutáveis ??para aqueles. Como Brian colocá-lo elloquently, legibilidade não é intrínseco. Esta versão é a minha preferência para este caso particular, e parece sentar em algum lugar no meio.

A uma Clojure é mais complicado à primeira vista; embora talvez mais elegante. OO é o resultado de fazer linguagem mais "compreensível" a nível superior. linguagens funcionais parece ter um "algorithimc" mais (/ elementar primitiva) sensação. Isso é apenas o que eu sentia no momento. Talvez isso vai mudar quando eu tenho mais experiência de trabalho com clojure.

Eu tenho medo de que estamos decending para o jogo de que a linguagem pode ser o mais concisa ou resolver um problema no menor linha de código.

A questão são 2 dobras para mim:

  1. Como é fácil à primeira vista a obter uma sensação de que o código está fazendo ?. Isto é importante para os mantenedores de código.

  2. Como é fácil de adivinhar a lógica por trás do código ?. Muito detalhado / prolixo ?. Muito concisa?

"Faça tudo o mais simples possível, mas não mais simples."

Albert Einstein

Eu também estou aprendendo Clojure e amá-la. Mas nesta fase do meu desenvolvimento, a versão Groovy era mais fácil de entender. O que eu gosto sobre Clojure, porém, é a leitura do código e ter o "Aha!" experiência quando você finalmente "pegar" o que está acontecendo. O que eu realmente desfrutar é a experiência similar que acontece a poucos minutos mais tarde, quando você percebe todas as maneiras o código poderia ser aplicada a outros tipos de dados sem alterações no código. Perdi a conta do número de vezes que eu trabalhei por algum código numérico em Clojure e, em seguida, um pouco mais tarde, pensou em como esse mesmo código pode ser usado com cordas, símbolos, widgets, ...

A analogia que eu uso é sobre cores de aprendizagem. Lembra quando você foi apresentado a cor vermelha? Você entendeu muito rapidamente - há toda essa coisa vermelha no mundo. Então você ouviu o magenta prazo e foram perdidos por um tempo. Mas, novamente, depois de um pouco mais exposição, você compreendeu o conceito e tinha uma maneira muito mais específico para descrever uma cor particular. Você tem que internalizar o conceito, segure um pouco mais informações em sua cabeça, mas você acaba com algo mais poderoso e concisa.

Groovy suporta vários estilos de resolver este problema demasiado:

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

definitivamente não é reformulado para ser bonita, mas não muito difícil de entender.

Eu sei que isto não é uma resposta para a pergunta, mas eu vou ser capaz "entender" o código muito melhor se houver testes, tais 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]

Isso vai me dizer, daqui a um ano, o que é esperado o código para fazer. Muito melhor do que qualquer versão excelente do código que eu tenho visto aqui.

Am I realmente fora do tópico?

A outra coisa é, eu acho "legibilidade" depende do contexto. Depende de quem vai manter o código. Por exemplo, a fim de manter a versão "funcional" do código Groovy (mais brilhante), levará não apenas a programadores Groovy, mas os programadores Groovy funcionais ... A outra, mais relevante, o exemplo é: se algumas linhas de código de torná-lo mais fácil de entender para "novato" Os programadores de Clojure, em seguida, o código vai global ser mais legível, porque ele vai ser compreendido por uma comunidade maior: não há necessidade de ter estudado Clojure por três anos para ser capaz de compreender as edições código e fazer a ele.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top