2010-08-08 15 views
7

Wenn ich folgend in clojureSind Clojure-Funktionen zyklische Abhängigkeiten, die vom Design ausdrücklich nicht zugelassen sind, oder handelt es sich nur um ein Leserverhalten?

(defn sub1a [a] 
    (cond 
    (= a 0) 0 
    true (sub1b (- a 1)))) 

(defn sub1b [a] 
    (cond 
    (= a 0) 0 
    true (sub1a (- a 1)))) 

(println (sub1a 10)) 

bekomme ich folgende Fehler tun:

java.lang.Exception: Unable to resolve symbol: sub1b in this context 

Aber wenn ich folgendes tun:

(defn sub1a [a] 
    (cond 
    (= a 0) 0 
    true (- a 1))) 

(defn sub1b [a] 
    (cond 
    (= a 0) 0 
    true (- a 1))) 

(defn sub1a [a] 
    (cond 
    (= a 0) 0 
    true (sub1b (- a 1)))) 

(defn sub1b [a] 
    (cond 
    (= a 0) 0 
    true (sub1a (- a 1)))) 

(println (sub1a 10)) 

Es ganz gut läuft.

Ist das Absicht oder nur eine Funktion der Funktionsweise des Clojure-Lesers?

Antwort

16

Sie können

(declare sub1a sub1b) 

‚erklären‘ gemeint zu tun ist speziell Erklärungen zur Herstellung von uns auf eine var ohne Bindungen zu schaffen.

Man erklärt die Namen:

(defn sub1a [a] 
    (cond 
    (= a 0) 0 
    true (sub1b (- a 1)))) 

(defn sub1b [a] 
    (cond 
    (= a 0) 0 
    true (sub1a (- a 1)))) 

(println (sub1a 10)) 

Auch die idomatic Weg, um die Standardbedingung in cond (für Clojure) die verwendet angeben: else-Klausel. Dies unterscheidet sich etwas von Common Lisp, das T (für True) verwendet. So kann Ihr vorheriger Code wie folgt umgeschrieben werden:

(defn sub1a [a] 
    (cond 
    (= a 0) 0 
    :else (sub1b (- a 1)))) 

... 
3

Die richtige Lösung ist wie von Rkrishnan geschrieben.

Was diesen Teil der Frage:

Is this by design, or just a function of the way the Clojure reader works?

Eigentlich ist das nichts mit dem Clojure Leser zu tun - es ist, weil der Compiler sofort Symbole zu Vars löst sie nach der Begegnung (in jenen Positionen, wo sie würden müssen "letztendlich" zu einem Var aufgelöst werden, im Gegensatz zu Orten, an denen sie Einheimische nennen, die zitiert oder an eine spezielle Form oder ein Makro übergeben werden, natürlich). Dies ist aus Effizienzgründen sinnvoll: Wenn man weiß, auf welchen Var ein Symbol zur Kompilierzeit verweist, kann man Code generieren, der zur Laufzeit keine Symbole auflösen muss (normalerweise müssen die Werte der Vars nachgeschlagen werden, nicht aber die Vars selbst). Wenn Sie wirklich wollen, können Sie Ihre Code resolve Symbole zur Laufzeit haben könnten:

(defn sub1a [a] 
    (cond 
    (= a 0) 0 
    :else ((resolve 'sub1b) (- a 1)))) 

(defn sub1b [a] 
    (cond 
    (= a 0) 0 
    :else ((resolve 'sub1a) (- a 1)))) 

(println (sub1a 10)) 

; prints 0 and returns nil 

Dies hat jedoch eine gewisse Verschlechterung der Leistung führen, die so gut wie nie in echtem Code gerechtfertigt ist, so Clojure macht Sie explizit über es, wenn Sie wirklich denken, das ist was Sie wollen.