Domanda

OK. Sono stato armeggiare con Clojure e ho continuamente incontrato lo stesso problema. Prendiamo questo piccolo frammento di codice:

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

Ora mi aspetto che questo per stampare una sequenza inizia con 128 come:

128
64
32
16
8
4
2

Al contrario, si tratta di un ciclo infinito, la stampa di 128 più e più volte. Chiaramente il mio effetto collaterale previsto non funziona.

Quindi, come faccio a ridefinire il valore di x in un ciclo come questo? Mi rendo conto che questo potrebbe non essere Lisp come (ho potuto utilizzare una funzione anonima che recurses su, di per sé, forse), ma se non a capire come impostare variabili come questo, ho intenzione di impazzire.

La mia altra ipotesi sarebbe quella di utilizzare impostare !, ma che dà "destinazione di assegnazione non valido", dato che non sono in una forma vincolante.

Per favore, mi illumini su come questo dovrebbe funzionare.

È stato utile?

Soluzione

def definisce un primo livello var, anche se lo si utilizza in una funzione o ciclo interno di un codice. Quello che si ottiene in let non sono vars. Per la documentazione per let :

  

La gente del posto creati con let non sono variabili. Una volta creati i loro valori non cambiano mai!

Non è necessario stato mutevole per il tuo esempio qui (enfasi non mio.); si potrebbe usare loop e recur.

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

Se si voleva essere di fantasia si potrebbe evitare il loop esplicita del tutto.

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

Se davvero ha voluto usare stato mutevole, una atomo potrebbe funzionare .

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

(Non avete bisogno di un do; while avvolge il corpo in un unico esplicito per voi.) Se si davvero voluto fare questo con vars che avrebbe dovuto fare qualcosa di orribile come questo.

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

Ma questo è molto brutto e non è Clojure idiomatica a tutti. Per utilizzare Clojure in modo efficace si dovrebbe cercare di smettere di pensare in termini di stato mutevole. Sarà sicuramente impazzire cercando di scrivere il codice Clojure in uno stile non funzionale. Dopo un po 'si può trovare ad essere una piacevole sorpresa come raramente è effettivamente necessario variabili mutabili.

Altri suggerimenti

Vars (che è quello che si ottiene quando si "def" qualcosa) non sono fatti per essere riassegnato (ma può essere):

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

Non c'è nulla ti impedisce di fare:

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

Se volete un "luogo" thread-locali impostabili è possibile utilizzare "vincolante" 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

Quindi, allora si può scrivere un ciclo come questo:

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

Ma penso che questo è abbastanza unidiomatic.

Se si pensa che avere variabili locali mutabili a funzioni pure sarebbe una bella comoda funzione che non fa male, perché la funzione rimane ancora puro, potreste essere interessati a questa discussione mailing list dove Rich Hickey spiega le sue ragioni per la loro rimozione dalla lingua. Perché non locali mutabili?

Parti di riferimento:

  

Se i locali erano variabili, vale a dire mutevole, quindi chiusure potrebbe chiudere su   stato mutevole, e, dato che le chiusure possono sfuggire (senza alcuni extra   divieto stesso), il risultato sarebbe filo-pericoloso. e la gente   sarebbe certamente farlo, ad esempio, pseudo-oggetti chiusura basati. Il risultato   sarebbe un enorme buco nell'approccio di Clojure.

     

Senza i locali mutevoli, le persone sono costrette ad utilizzare ripetersi, un funzionale   loop costrutto. Anche se questo può sembrare strano in un primo momento, è altrettanto   succinta come loop con la mutazione, e gli schemi risultanti possono essere   riutilizzato altrove in Clojure, cioè riscontrato, ridurre, modificare, ecc commutare   sono tutti (logicamente) molto simili. Anche se ho potuto rilevare e   prevenire mutando chiusure di fuggire, ho deciso di continuare in questo modo   per coerenza. Anche nel più piccolo contesto, i cicli non sono mutanti   più facile da capire ed eseguire il debug di mutazione quelli. In ogni caso, Vars   sono disponibili per l'uso al momento opportuno.

La maggior parte dei successivi messaggi preoccupazioni esecuzione una macro with-local-vars;)

Si potrebbe più usare idiomaticamente iterate e take-while invece,

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

(128 64 32 16 8 4 2)
user>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top