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

时间:2011-09-21 17:41:21

标签: clojure

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

(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"}
    ]
)

4 个答案:

答案 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>

“评估中止”意味着抛出异常。

您还可以将throwif的{​​{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))