作为我想做的事情的一个最小示例:
(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
在单元测试结束时。