Es gibt zwei Möglichkeiten, Ihre Frage anzugehen. Ich vermute jedoch, dass Sie, um das, was Sie tun möchten, vollständig zu automatisieren, Ihren eigenen benutzerdefinierten defn
Ersatz/Wrapper definieren müssen.
Die erste Sache zu erkennen ist, dass alle Funktionen anonym sind. Wenn wir geben:
(defn hello [] (println "hi"))
wir wirklich eingeben:
(def hello (fn [] (println "hi"))
wir ein Symbol hello
schaffen, die zu einem anonymen var
Punkte, die wiederum auf eine anonyme Funktion. Wir können jedoch die Funktion einen „internen Namen“ wie so geben:
(def hello (fn fn-hello [] (println "hi")))
So, jetzt können wir die Funktion von außen über hello
oder von innen Zugriff auf entweder hello
von fn-hello
Symbole (bitte nicht immer Verwenden Sie hello
in beiden Standorten oder Sie schaffen eine Menge Verwirrung ... obwohl es legal ist).
ich häufig die fn-hello
Methode (sonst) anonyme Funktionen verwenden, da geworfen Ausnahmen das fn-hello
Symbol enthalten, die die Quelle des Problems wesentlich erleichtert das Aufspüren (die Zeilennummer des Fehlers fehlt oft aus dem Stack-Trace). Zum Beispiel, wenn Instaparse verwendet, müssen wir eine Karte von anonymen Transformationsfunktionen wie:
{
:identifier fn-identifier
:string fn-string
:integer (fn fn-integer [arg] [:integer (java.lang.Integer. arg)])
:boolean (fn fn-boolean [arg] [:boolean (java.lang.Boolean. arg)])
:namespace (fn fn-namespace [arg] [:namespace arg])
:prefix (fn fn-prefix [arg] [:prefix arg])
:organization (fn fn-organization [arg] [:organization arg])
:contact (fn fn-contact [arg] [:contact arg])
:description (fn fn-description [arg] [:description arg])
:presence (fn fn-presence [arg] [:presence arg])
:revision (fn fn-revision [& args] (prepend :revision args))
:iso-date (fn fn-iso-date [& args] [:iso-date (str/join args)])
:reference (fn fn-reference [arg] [:reference arg])
:identity (fn fn-identity [& args] (prepend :identity args))
:typedef (fn fn-typedef [& args] (prepend :typedef args))
:container (fn fn-container [& args] (prepend :container args))
:rpc (fn fn-rpc [& args] (prepend :rpc args))
:input (fn fn-input [& args] (prepend :input args))
...<snip>...
}
und gibt jede Funktion die „interne Bezeichnung“ viel macht das Debuggen, viel einfacher. Vielleicht wäre dies unnötig, wenn Clojure bessere Fehlermeldungen hätte, aber das ist ein langjähriger (& bisher nicht erfüllter) Wunsch.
können Sie weitere Informationen finden Sie hier: https://clojure.org/reference/special_forms#fn
Wenn Sie genau lesen, es behauptet, dass (defn foo [x] ...)
expandiert in
(def foo (fn foo [x] ...))
obwohl Sie experimentieren müssen, um zu sehen, ob dies bereits die Einsatz- gelöst Fall, den Sie suchen. Es funktioniert so oder so wie in diesem Beispiel zu sehen, wo wir die inneren fn-fact
Namen explizit vermeiden:
(def fact (fn [x] ; fn-fact omitted here
(if (zero? x)
1
(* x (fact (dec x))))))
(fact 4) => 24
Diese Version funktioniert auch:
(def fact (fn fn-fact [x]
(if (zero? x)
1
(* x (fn-fact (dec x))))))
(fact 4) => 24
(fn-fact 4) => Unable to resolve symbol: fn-fact
So sehen wir, dass die „interne Bezeichnung“ fn-fact
innen versteckt die Funktion und ist von außen unsichtbar.
Ein zweiter Ansatz, wenn ein Makro verwendet wird, ist die &form
globalen Daten zu verwenden, die Zeilennummer aus dem Quellcode zugreifen. In the Tupelo library diese Technik verwendet wird Fehlermeldungen für die
(defmacro dotest [& body] ; #todo README & tests
(let [test-name-sym (symbol (str "test-line-" (:line (meta &form))))]
`(clojure.test/deftest ~test-name-sym [email protected])))
Diese Bequemlichkeit Makro erlaubt die Verwendung von Unit-Tests wie zu verbessern:
(dotest
(is (= 3 (inc 2))))
, die
auswertet
(deftest test-line-123 ; assuming this is on line 123 in source file
(is (= 3 (inc 2))))
statt manuell eingeben
(deftest t-addition
(is (= 3 (inc 2))))
Sie können (:line (meta &form))
und andere Informationen in jedem Makro zugreifen, die Ihre Fehlermeldungen machen und/oder Ausnahmen viel informativer der schlechten Leser versuchen, ein Problem zu debuggen.
Neben dem obigen Makro Wrapper Beispiel, ein anderes (komplizierter) Beispiel der gleichen Technik can be seen in the Plumatic Schema library, wo sie clojure.core/defn
mit einer erweiterten Version wrap.
Sie können auch wünschen, diese Frage zur Klärung an sehen, wie Clojure verwendet den „anonymous“ var als Vermittler zwischen einem Symbol und einer Funktion: When to use a Var instead of a function?
Nicht, dass es einen Unterschied macht, aber '(defn hallo [] (println "hi")) 'ist wirklich' (def hallo (fn hallo [] (println "hi")) ', da die Funktion rekursiv sein darf Der lokale Name scheint völlig vergessen zu sein. – Thumbnail
@AlanThompson vielen dank! ich habe gerade gelernt, hier viel mehr als nur die punktuellen Frage, und diese Diskussion war wirklich ausgezeichnet – matanster
@thumbnail und AlanThompson ich glaube nicht das ursprüngliche ersten paar Kommentare hinzufügen ausgetauscht jede info über die antwort wie gerade formuliert ... darf ich vorschlagen sie einfach jetzt (löschend) fallen zu lassen, da sie nach der aktualisierung der antwort etwas verwirrend sind, wenn ich sie nicht falsch interpretiert habe. – matanster