以下是我想要开始工作的示例代码:
(letfn [(CONC [f] f)
(CONT [f] (str "\newline" f))]
((voodoo "CONC") "hamster"))
是否有一些voodo会让它以仓鼠为参数调用CONC函数?也就是说,有没有办法将字符串“CONC”转换为未绑定到命名空间而不是绑定到本地绑定的函数?
编辑:
更清楚的是,这将被称为:
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
答案 0 :(得分:6)
我可能通过创建一个由字符串索引的函数映射来解决这个问题:
(def voodoo
{"CONC" (fn [f] f)
"CONT" (fn [f] (str "\newline" f))})
然后你想要的代码应该直接工作(利用map是查找它的参数的函数这一事实)
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
请注意,这里的函数是完全匿名的 - 您不需要在命名空间中的任何位置引用它们以使其工作。在我看来这是一件好事,因为除非你还需要其他地方的功能,否则最好避免过多地污染你的顶级命名空间。
答案 1 :(得分:4)
没有。 Eval永远无法访问本地/词汇环境。
编辑:这不是一个很好的答案,也不是很准确。您可以将voodoo编写为宏,然后它不需要运行时访问词法环境,只需要编译时。但是,这意味着只有在编译时知道要调用的函数是x
才会有效,因此它不会非常有用 - 为什么不只是键入x
而不是{ {1}}?
(voodoo "x")
答案 2 :(得分:4)
嗯,这有点可能:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(throw (RuntimeException. "no such local")))))
...现在我们可以做这样奇怪的事情:
user> (defn example [s]
(letfn [(foo [x] {:foo x})
(bar [x] {:bar x})]
((voodoo s) :quux)))
#'user/example
user> (example "foo")
{:foo :quux}
user> (example "bar")
{:bar :quux}
user> (example "quux")
; Evaluation aborted.
user> *e
#<RuntimeException java.lang.RuntimeException: no such local>
“评估中止”意味着抛出异常。
您还可以将throw
中if
的{{1}}分支替换为voodoo
,如果没有找到本地,则将其推迟到全局变量:
(resolve (symbol ~s))
...现在这适用于上面(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(resolve (symbol ~s)))))
的定义(但请注意,如果您正在尝试REPL,则需要在重新定义example
后重新编译example
) :
voodoo
现在,这是对Clojure设施的滥用,人们可以尽力去做。如果一个人不能,那么迈克尔·福格斯可能会转向evalive;它是一个库,它以user> (defn quux [x] {:quux x})
#'user/quux
user> (example "quux")
{:quux :quux}
函数和几个实用程序的形式提供“eval-with-locals”工具。功能似乎也是很好的因素,例如,上面的evil
之类的内容被封装为一个宏,~(zipmap ...)
似乎几乎是evil
的替代品(添加env参数,你很高兴) 。我没有正确阅读来源,但我现在可能会,看起来很有趣。 : - )
答案 3 :(得分:2)
我不太清楚你要求的是什么所以我会尝试几个答案:
如果您的字符串是您要调用的函数的名称:
(def name "+")
((find-var (symbol (str *ns* "/" name))) 1 2 3)
这会给伏都教这样一种解释:
(defn voodoo [name args] (apply (find-var (symbol (str *ns* "/" name))) args))
#'clojure.core/voodoo
clojure.core=> (voodoo "+" [1 2 3])
6
clojure.core =&GT;
假设您的函数位于当前名称空间ns
。
如果您想将字符串转换为函数,可以使用此模式
(let [f (eval (read-string "(fn [] 4)"))] (f))