2014-04-23 5 views
7

Clojure-Stil (und gute Software-Entwicklung im Allgemeinen) legt Wert auf viele kleine Funktionen, von denen eine Teilmenge öffentlich sichtbar ist, um eine externe Schnittstelle zur Verfügung zu stellen.Clojure style: defn vs. leftn

In Clojure scheint es ein paar Möglichkeiten, dies zu tun:

(letfn [(private-a ...) 
     (private-b ...)] 
    (defn public-a ...) 
    (defn public-b ...)) 

(defn- private-a ...) 
(defn- private-b ...) 
(defn public-a ...) 
(defn public-b ...) 

Die letfn Form ausführlicher und vielleicht weniger flexibel scheint, aber es reduziert den Umfang der Funktionen.

Meine Vermutung ist, dass Letfn nur für den Einsatz in anderen Formen gedacht ist, wenn kleine Hilfsfunktionen nur in einem kleinen Bereich verwendet werden. Ist das der Konsens? Sollte ich jemals auf einer Top-Level verwendet werden (wie ich zuvor empfohlen habe)? Wann sollte es verwendet werden?

Antwort

11

letfn für den Einsatz in den Fällen der gegenseitigen Rekursion bestimmt ist:

(letfn [(is-even? [n] 
      (if (zero? n) 
      true 
      (is-odd? (dec n)))) 
     (is-odd? [n] 
      (if (zero? n) 
      false 
      (is-even? (dec n))))] 
    (is-even? 42)) 

;; => true 

Sie es nicht auf der obersten Ebene verwenden.

Verwenden Sie das Makro defn auch nicht an anderen Stellen als auf der obersten Ebene, es sei denn, Sie haben ganz bestimmte Gründe. Es wird auf die spezielle Form def erweitert, die globale Variable erstellen und internieren wird.

+0

Fühlst du, dass "Letfn" generell nicht für Hilfsfunktionen geeignet ist - man benutze user2494739 - aber nur für gegenseitige Rekursion? Ich habe kein Gespür dafür, was üblicherweise mit "leftn" gemacht wird, also mag ich eigensinnig sein, aber ich definiere oft Hilfsfunktionen mit 'leftn' oder manchmal' (let [f (fn [x] ...)). ..] 'zu vermeiden,' Let' und 'Letfn' zusammen zu verwenden. – Mars

+0

Ich denke mit' letfn' oder '(lass [f (fn [x] ...' so ist gut. Ich mache das die ganze Zeit selbst Es reduziert die Anzahl der Funktionen, die Sie auf der obersten Ebene definiert haben, was ich für eine gute Sache halte. –

+0

Um zu verdeutlichen, benutze ich 'leftn' * innerhalb * a' defn'. Etwas wie '(defn top-level-fn [xs] (letfn [(helfer [y] (do-something-withy))] (map helper xs)))' –

4

Der Zweck von letfn ist völlig anders als der Zweck der defn Form. Die Verwendung von letfn auf der obersten Ebene gibt Ihnen nicht die gleichen Eigenschaften von defn, da jede Bindung von Namen an Funktionen, die innerhalb von letfn gebunden sind, außerhalb ihres Gültigkeitsbereichs nicht sichtbar ist. Die Bindung für innerhalb von let oder letfn gebundene Funktionen ist außerhalb ihres lexikalischen Geltungsbereiches nicht verfügbar. Außerdem ist die Sichtbarkeit von Funktionen innerhalb von letfn unabhängig von der Reihenfolge, in der sie innerhalb dieses lexikalischen Bereichs gebunden sind. Das ist nicht der Fall mit let.

+0

@Thumbnail Vars war ein falscher Begriff zu verwenden. Ich habe versucht, die Bedingungen zu beheben. –

2

Meine Regeln sind folgende:

  • Wenn eine Tochtergesellschaft Funktion in ein öffentlichen verwendet wird, definieren sie lokal mit let oder letfn.
  • Wenn es in mehreren verwendet wird, definieren Sie es auf oberster Ebene mit defn-.

Und

  • nicht let oder letfn auf höchster Ebene anwenden.
  • Verwenden Sie nicht def oder defn oder defn- an einer anderen Stelle als auf der obersten Ebene.
+0

Ich benutze einen Kanal, um die Kommunikation zwischen ein paar privaten Funktionen und meinem zu ermöglichen Hauptfunktion und Nutzung: '(let [c (chan)] (defn- a ...) (defn- b ...) (defn -main (a ...) (b ...))) ' wäre das nicht gut? – peter

+0

@peter Ich würde nicht sagen, dass es zu schade ist, wenn Sie diese Veränderlichkeit und Staatslosigkeit WIRKLICH brauchen. Seien Sie gewarnt, es wird Probleme mit Nebenläufigkeit verursachen. – seequ