如何将符号传递给函数以在clojure中创建函数



作为我想做的事情的一个最小示例:

(defn mkfn [func]
(fn func [a] (print "I am a function")))
(mkfn 'x) ; => #function[user/mkfn/func--10871]
(type x)
(x)

最后两个结果都是:

Syntax error compiling at (conjure-log-12628.cljc:1:1).
Unable to resolve symbol: x in this context

我不知道为什么这不起作用,因为fn以符号为输入,而'x是一个符号。我也不知道如何完成这项任务。

就此而言:

user=> (def (eval 'y) 3)
Syntax error compiling def at (conjure-log-12628.cljc:1:1).
user=> (def 'y 3)
Syntax error compiling def at (conjure-log-12628.cljc:1:1).
First argument to def must be a Symbol
First argument to def must be a Symbol
user=> (type 'y)
clojure.lang.Symbol

其他不起作用的东西:

(defn mkfn [func]
(fn (sympol func) [a] (print "i am a function")))
(symbol "y") ; => y ; a symbol
(def (symbol "y") 3) ; => an err

您可能需要一个宏。似乎您想用提供的名称调用该函数,因此还必须将fn替换为defn

您必须小心多个参数,因为具有参数向量[a]的函数x必须使用一个参数调用,而不像(x)那样。

(defmacro mkfn [func]
`(defn ~func [~'a] 
(print "I am a function")))
(mkfn x)
=> #'user/x
(x 1)
I am a function=> nil

还有其他方法,使用intern,这样就可以完全避免编写宏:

(intern *ns* 'x (fn [a] (print "I am a function")))
=> #object...
(x 1)
I am a function=> nil

intern示例:

(defn mkfn [func]
(intern *ns* func (fn [a] (print "I am a function"))))
=> #'user/mkfn
(mkfn 'y)
=> #'user/y
(y 1)
I am a function=> nil

至于你的错误,def是一种特殊的形式,所以它有不同的评估规则。它不计算第一个参数,它必须是一个符号,并且(未计算的((eval 'y)'y(symbol "y")不是符号,而y是。

您需要一个宏,因为您需要编写代码。

(defmacro mkfn [func]
`(fn ~func [~'a] ...))

有两种方法,要么是函数加eval,要么是宏。如果你真的想用你选择的名称用程序创建一个新函数,那么宏解决方案就是最好的选择。

函数+eval解决方案很有指导意义,但您必须在调用函数时引用函数名称(通过第二个eval(,或者在创建函数时将创建的函数保存在另一个变量中。

如果你对编写宏感兴趣,请先看看另一个问题:如何编写Clojure线程宏?

对于函数+eval,我们可以从我最喜欢的模板项目开始,添加以下内容:

(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(defn maker-eval
[fn-sym]
(let [ll (list 'fn 'anon-fn [] (str "I am " fn-sym))]
(spyx :eval ll)
(eval ll)))
(verify
(let [anon-fn-1 (maker-eval 'aaa)]
(is= "I am aaa" (anon-fn-1))) ; need the temp local variable
(let [anon-fn-2 (maker-eval 'bbb)]
(is= "I am bbb" (anon-fn-2))) ; need the temp local variable
)

我们可以看到该功能的创建和使用,以及打印输出:

:eval ll => (fn anon-fn [] "I am aaa")
:eval ll => (fn anon-fn [] "I am bbb")

对于宏版本,我们键入

(defn maker-macro-impl
[fn-sym]
(let [ll `(defn ~fn-sym [] (str "I am " (str (quote ~fn-sym))))]
(spyx :macro ll)
ll))
(defmacro maker-macro
[fn-sym] (maker-macro-impl fn-sym))
(verify
(let [anon-fn-3 (maker-macro-impl 'ccc)]
(is= anon-fn-3 (quote
(clojure.core/defn ccc [] (clojure.core/str "I am " (clojure.core/str (quote ccc)))))))
(maker-macro ddd)
(is= (ddd) "I am ddd"))

请参阅打印的:

:macro ll => (clojure.core/defn ccc [] (clojure.core/str "I am " (clojure.core/str (quote ccc))))

请注意,局部变量anon-fn-3仅用于测试maker-macro-impl函数,而不需要调用新创建的函数ddd在单元测试结束时。

最新更新