Pergunta

OK. Fui mexer com Clojure e eu sempre correr para o mesmo problema. Vamos dar este pequeno fragmento de código:

(let [x 128]
  (while (> x 1)
    (do
      (println x)
      (def x (/ x 2)))))

Agora eu esperava isso para imprimir uma seqüência começando com 128 assim:

128
64
32
16
8
4
2

Ao contrário, é um loop infinito, imprimindo 128 mais e mais. Claramente meu efeito colateral destina-se não está funcionando.

Então, como é que eu vou para redefinir o valor de x em um loop como este? Sei que isso pode não ser Lisp como (eu poderia usar uma função anônima que recurses nela é auto, talvez), mas se eu não descobrir como variável set como este, eu vou enlouquecer.

Meu outro palpite seria a criação uso !, mas que dá "destino da atribuição inválida", já que eu não estou em uma forma de ligação.

Por favor, me esclareça sobre como isto é suposto trabalho.

Foi útil?

Solução

def define um nível superior var, mesmo se você usá-lo em uma função ou circuito interno de algum código. O que você recebe em let não são Vars. Por a documentação para let :

Locals criados com let não são variáveis. Uma vez criados os seus valores nunca mudança!

(não Ênfase minha.) Você não precisa estado mutável para o seu exemplo aqui; você poderia usar loop e recur.

(loop [x 128]
  (when (> x 1)
    (println x)
    (recur (/ x 2))))

Se você queria ser extravagante você poderia evitar o loop explícita inteiramente.

(let [xs (take-while #(> % 1) (iterate #(/ % 2) 128))]
  (doseq [x xs] (println x)))

Se você realmente queria usar estado mutável, um átomo trabalho poder .

(let [x (atom 128)]
  (while (> @x 1)
    (println @x)
    (swap! x #(/ %1 2))))

(Você não precisa de um do; while envolve seu corpo em uma explícita para você.) Se você realmente queria fazer isso com vars você teria que fazer algo horrível assim.

(with-local-vars [x 128]
  (while (> (var-get x) 1)
    (println (var-get x))
    (var-set x (/ (var-get x) 2))))

Mas isso é muito feio e não é Clojure idiomática em tudo. Para usar Clojure efetivamente você deve tentar parar de pensar em termos de estado mutável. Ele vai certamente deixá-lo louco tentando escrever código Clojure em um estilo não-funcional. Depois de um tempo você pode encontrá-lo para ser uma agradável surpresa como raramente você realmente precisa variáveis ??mutáveis.

Outras dicas

Vars (que é o que você começa quando você "def" algo) não são destinadas a ser transferido (mas pode ser):

user=> (def k 1)
#'user/k
user=> k
1

Não há nada que você parar de fazer:

user=> (def k 2)
#'user/k
user=> k
2

Se você quer um "lugar" de segmento local configurável você pode usar "ligação" e "set!":

user=> (def j) ; this var is still unbound (no value)
#'user/j
user=> j
java.lang.IllegalStateException: Var user/j is unbound. (NO_SOURCE_FILE:0)
user=> (binding [j 0] j)
0

Então você pode escrever um loop como este:

user=> (binding [j 0]
         (while (< j 10)
           (println j)
           (set! j (inc j))))
0
1
2
3
4
5
6
7
8
9
nil

Mas eu acho que isso é muito unidiomatic.

Se você acha que ter variáveis ??locais mutáveis ??em funções puras seria um recurso conveniente agradável que não faz mal, porque a função ainda permanece pura, você poderia estar interessado nesta discussão lista de discussão onde rico Hickey explica suas razões para removê-los a partir da linguagem. Por que não locals mutáveis?

parte relevante:

Se moradores foram variáveis, ou seja, mutável, então encerramentos poderão fechar sobre estado mutável, e, dado que os fechamentos podem escapar (sem algum extra proibição de mesmo), o resultado seria thread-inseguro. E pessoas certamente fazê-lo, por exemplo, pseudo-objectos à base de fecho. O resultado seria um enorme buraco na abordagem de Clojure.

Sem locals mutáveis, as pessoas são forçadas a usar recorrência, um funcional looping construo. Enquanto isto pode parecer estranho à primeira vista, é tão sucinta como loops com mutação, e os padrões resultantes podem ser reutilizada em outras posições do Clojure, isto é RECUR, reduzir, alterar, comutar etc. são todos (logicamente) muito semelhante. Mesmo que eu poderia detectar e Prevenir mutação fechamentos de escapar, eu decidi mantê-lo dessa maneira para consistência. Mesmo no menor contexto, lacetes não mutação são fácil de entender e de depuração de mutação queridos. Em qualquer caso, Vars estão disponíveis para uso, quando apropriado.

A maioria dos postos preocupações posteriores execução uma macro with-local-vars;)

Você poderia mais idiomaticamente usar iterate e take-while em vez disso,

user> (->> 128
           (iterate #(/ % 2))
           (take-while (partial < 1)))

(128 64 32 16 8 4 2)
user>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top