将字符串转换为clojure中不在命名空间中的函数

将字符串转换为clojure中不在命名空间中的函数,clojure,Clojure,下面是我想开始工作的示例代码: (letfn [(CONC [f] f) (CONT [f] (str "\newline" f))] ((voodoo "CONC") "hamster")) 有没有什么巫术能让它以仓鼠为参数调用CONC函数?也就是说,是否有某种方法可以将字符串“CONC”转换为不绑定到命名空间而是绑定到本地绑定的函数 编辑: 更清楚地说,这将被称为: (map #((voodoo (:tag %)) (:value %)) [ {

下面是我想开始工作的示例代码:

(letfn [(CONC [f] f)
        (CONT [f] (str "\newline" f))]
((voodoo "CONC") "hamster"))
有没有什么巫术能让它以仓鼠为参数调用CONC函数?也就是说,是否有某种方法可以将字符串“CONC”转换为不绑定到命名空间而是绑定到本地绑定的函数

编辑:

更清楚地说,这将被称为:

(map #((voodoo (:tag %)) (:value %)) 
    [ 
        {:tag "CONC" :value "hamster"} 
        {:tag "CONT" :value "gerbil"}
    ]
)

我不太清楚你在问什么,所以我会尝试几个答案:

如果有一个字符串是要调用的函数的名称:

 (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
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>
clojure.core=> 这假定您的函数位于当前名称空间
ns

如果要将字符串转换为函数,可以使用此模式

(let [f (eval (read-string "(fn [] 4)"))] (f))

否。Eval从未访问过本地/词汇环境

编辑:这不是一个很好的答案,也不是很准确。您可以将voodoo编写为宏,这样它就不需要运行时访问词法环境,只需要编译时。但是,这意味着只有在编译时知道要调用的函数是
x
,它才会起作用,因此它不会很有用-为什么不直接键入
x
,而不是
(巫毒“x”)


我可能会通过创建一个由字符串索引的函数映射来解决这个问题:

(def voodoo 
  {"CONC" (fn [f] f)
   "CONT" (fn [f] (str "\newline" f))})
然后,所需的代码应该直接工作(利用映射是一个查找其参数的函数这一事实)


请注意,这里的函数是完全匿名的——您不需要在名称空间中的任何位置引用它们就可以工作。在我看来,这是一件好事,因为除非您在其他地方也需要函数,否则最好避免过多地污染顶级命名空间。

嗯,有可能:

(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")))))
…现在我们可以做这样奇怪的事情:

(defn voodoo [name args] (apply (find-var (symbol (str *ns* "/" name))) args))
#'clojure.core/voodoo
clojure.core=> (voodoo "+" [1 2 3])
6
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>
…现在这适用于上述
示例
的定义(但请注意,如果您在REPL进行实验,则需要在重新定义
巫毒后重新编译
示例
):


现在,这是对Clojure设施的滥用,如果没有它,我们会做得很好。如果做不到这一点,人们可能应该求助于迈克尔·福格斯的著作;它是一个以
evil
函数和一些实用程序的形式提供“本地评估”功能的库。该功能似乎也得到了很好的考虑,例如上面的
~(zipmap…
之类的东西被封装为一个宏,
evil
几乎可以替代
eval
(添加env参数,就可以了)。我没有正确阅读源代码,但我现在可能会,看起来很有趣。:-)

(用Clojure 1.3-RC0测试)实际上,如果你足够邪恶地使用它,一个真正的黑色伏都教是可能的——参见和福格斯的。:-)+1--我的答案只涉及如何实现
巫毒
,但在大多数情况下,这肯定是更明智的解决方案。这可能是项目的最佳答案,也是访问未绑定在命名空间中的函数的有趣答案
(defmacro voodoo [s]
  (let [env (zipmap (map (partial list 'quote) (keys &env))
                    (keys &env))]
    `(if-let [v# (~env (symbol ~s))]
       v#
       (resolve (symbol ~s)))))
user> (defn quux [x] {:quux x})
#'user/quux
user> (example "quux")
{:quux :quux}