Clojure 获取函数';其主体中的名称或:测试主体
在clojure中,是否可以习惯性地在函数体中获取函数名,希望在不引入新的函数定义包装器的情况下实现这一点?还可以在函数的Clojure 获取函数';其主体中的名称或:测试主体,clojure,Clojure,在clojure中,是否可以习惯性地在函数体中获取函数名,希望在不引入新的函数定义包装器的情况下实现这一点?还可以在函数的:test属性的主体内访问函数名吗 出于动机,这有助于某些日志记录情况,也有助于保持:test的主体不受所提供函数名称更改的影响 下面简要说明meta得到的最接近值;据我所知,在clojure中没有这个概念提供给meta (defn a [] (:name (meta (var a)))) 显然,使用包装器宏很容易实现 编辑:幸运的是,到目前为止还没有人提到lambda c
:test
属性的主体内访问函数名吗
出于动机,这有助于某些日志记录情况,也有助于保持:test
的主体不受所提供函数名称更改的影响
下面简要说明meta
得到的最接近值;据我所知,在clojure中没有这个概念提供给meta
(defn a [] (:name (meta (var a))))
显然,使用包装器宏很容易实现
编辑:幸运的是,到目前为止还没有人提到lambda combinator。有两种方法来解决您的问题。然而,我怀疑要完全自动化您想要做的事情,您需要定义自己的定制defn
replacement/wrapper
首先要认识到的是,所有函数都是匿名的。当我们键入:
(defn hello [] (println "hi"))
我们真的在打字:
(def hello (fn [] (println "hi"))
我们正在创建一个符号hello
,该符号指向一个匿名var
,该变量反过来指向一个匿名函数。但是,我们可以给函数一个“内部名称”,如下所示:
因此,现在我们可以通过hello
从外部访问该功能,或者从内部使用hello
或fn hello
符号访问该功能(请不要在两个位置都使用hello
,否则会造成很多混乱……即使这是合法的)
我经常在(否则)匿名函数中使用fn hello
方法,因为抛出的任何异常都将包含fn hello
符号,这使得跟踪问题源变得更加容易(堆栈跟踪中经常缺少错误的行号)。例如,使用Instaparse时,我们需要匿名转换函数的映射,如:
{
: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>...
}
尽管您可能需要进行实验,看看这是否已经解决了您正在寻找的用例。它以任何一种方式工作,如本例所示,我们明确避免了内部的fn事实
名称:
(def fact (fn [x] ; fn-fact omitted here
(if (zero? x)
1
(* x (fact (dec x))))))
(fact 4) => 24
此版本也适用于:
(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
因此,我们看到“内部名称”fn fact
隐藏在函数内部,从外部看不到
如果使用宏,第二种方法是使用&form
全局数据从源代码访问行号。此技术用于改进服务器的错误消息
(defmacro dotest [& body] ; #todo README & tests
(let [test-name-sym (symbol (str "test-line-" (:line (meta &form))))]
`(clojure.test/deftest ~test-name-sym ~@body)))
此方便宏允许使用单元测试,如:
(dotest
(is (= 3 (inc 2))))
哪一种评估是正确的
(deftest test-line-123 ; assuming this is on line 123 in source file
(is (= 3 (inc 2))))
而不是手工打字
(deftest t-addition
(is (= 3 (inc 2))))
您可以访问任何宏中的(:line(meta&form))
和其他信息,这些信息可以使错误消息和/或异常对试图调试问题的差劲读者提供更多信息
除了上面的宏包装器示例之外,还有另一个(更复杂的)相同技术的示例,它们使用扩展版本包装clojure.core/defn
您可能还希望查看此问题,以澄清Clojure如何使用“匿名”变量作为符号和函数之间的中介:并不是说它有什么区别,而是(defn hello[](println“hi”)
实际上是(def hello(fn hello[](println“hi”))
,因为函数允许递归。本地名称似乎被完全忘记了。@阿兰·汤普森非常感谢!我在这里学到了很多东西,不仅仅是逐点的问题,这次讨论真的很有趣excellent@thumbnail阿兰·汤普森:我不认为最初交换的几条评论会在网上增加任何信息nswer目前的措辞……我是否可以建议立即删除它们(?)如果我没有曲解任何东西的话,因为答案被相应地更新后,它们现在有点令人困惑。有一条评论是,对于我原始问题的宏解决方案,我发现只需将defn
,这样宏就可以将函数名填充到函数体中,相当于o这个答案中的宏示例。meta
甚至不需要,尽管这无疑是很好的、实用的测试包装灵感!是的,我认为围绕defn
的宏包装是实现目标的最佳方式。有关Plumatic模式示例的链接,请参阅上面的编辑。
(deftest test-line-123 ; assuming this is on line 123 in source file
(is (= 3 (inc 2))))
(deftest t-addition
(is (= 3 (inc 2))))