2012-07-30 11 views
24

ho cercato di capire le variabili dinamiche e la funzione di legame così ho provato questo (clojure 1.3):clojure e ^: dinamica

user=> (defn f [] 
      (def ^:dynamic x 5) 
      (defn g [] (println x)) 
      (defn h [] (binding [x 3] (g))) 
      (h)) 
#'user/f 
user=> (f)  
5 
nil 

confuso, ho provato questo codice un po 'più semplice:

user=> (def ^:dynamic y 5) 
#'user/y 
user=> (defn g [] (println y)) 
#'user/g 
user=> (defn h [] (binding [y 3] (g))) 
#'user/h 
user=> (h) 
3 
nil 

Cosa è la differenza tra i due pezzi di codice? Perché il secondo esempio funziona ma il primo no?

Suggerimento: Ho appena realizzato che le seguenti opere (ancora non comprendere appieno il motivo):

user=> (def ^:dynamic y 5) 
#'user/y 
user=> (defn f [] (defn g [] (println y)) (defn h [] (binding [y 3] (g))) (h)) 
#'user/f 
user=> (f) 
3 
nil 
user=> 

risposta

26

ottengo 3 come risultato (come ci si aspetterebbe) quando faccio funzionare il vostro primo esempio in Clojure 1.4 .... hai provato questo con un nuovo REPL?

^:dynamic è un'istruzione per il compilatore Clojure che un simbolo (come definito con def) è destinato a essere dinamicamente rimbalzo (con binding).

Esempio:

(def foo 1) 
(binding [foo 2] foo) 
=> IllegalStateException Can't dynamically bind non-dynamic var: ... 

(def ^:dynamic bar 10) 
(binding [bar 20] bar) ;; dynamically bind bar within the scope of the binding 
=> 20 
bar      ;; check underlying value of bar (outside the binding) 
=> 10 

noti che binding ha portata dinamico all'interno del thread chiamante - qualsiasi funzione chiamata all'interno del legame vedrà il valore modificato della bar (20), ma qualsiasi altro thread sarà ancora vedere il invariata valore di radice di 10.

Infine un paio di punti di stile che si possono trovare utili:

  • E 'generalmente c cattiva idea per mettere def e defn all'interno delle funzioni in quanto influenzano lo spazio dei nomi che racchiude. All'interno delle funzioni dovresti usare invece (let [foo bar] ...).
  • Quando si ritiene di voler utilizzare binding, in genere si dovrebbe considerare se è possibile ottenere lo stesso risultato utilizzando le funzioni di ordine superiore. binding è utile in alcuni contesti, ma in generale non è un buon modo per passare i parametri - la composizione della funzione di solito è migliore nel lungo periodo. La ragione di ciò è che binding crea un contesto implicito richiesto per l'esecuzione della tua funzione e questo può essere difficile da testare/eseguire il debug.
+0

Comprendo i pro/contro del binding. Mi rendo anche conto che il primo esempio di codice è un codice clojure insolito. Quello che non ho capito è perché non ha funzionato (con 1.3, nuovo sostituto). – Kevin

+0

Ho difficoltà a vedere quando vorrai legare! Sembra anatema per il modo funzionale. Cosa mi manca? – Hendekagon

+1

@Hendekagon - probabilmente merita una domanda SO a se stante. Ma ho trovato utile come un modo in più di passare il contesto attorno al debugging/working al REPL - se lo facessi in modo puramente funzionale, avresti bisogno di inserire nuovi parametri lungo una chiamata (potenzialmente molto lunga) grafico. – mikera