如何自动创建Clojure`defn`函数? 更新:
我重新编写了这篇文章,以使原来的问题更清楚,并添加了所有宏解决方案。请忽略此版本,并参考以下内容: 很遗憾,SO不允许我删除此旧版本如何自动创建Clojure`defn`函数? 更新:,clojure,macros,code-generation,Clojure,Macros,Code Generation,我重新编写了这篇文章,以使原来的问题更清楚,并添加了所有宏解决方案。请忽略此版本,并参考以下内容: 很遗憾,SO不允许我删除此旧版本 最初出于以下问题: 假设您希望自动创建许多类似的函数(即,不必全部手写)。假设我们有一些预先存在的数据,并希望编写如下访问器函数: (def foo {:able "Adelicious!" :baker "Barbrallicious!" :charlie "Charlizable"}) (def bar {:able
最初出于以下问题:
假设您希望自动创建许多类似的函数(即,不必全部手写)。假设我们有一些预先存在的数据,并希望编写如下访问器函数:
(def foo
{:able "Adelicious!"
:baker "Barbrallicious!"
:charlie "Charlizable"})
(def bar
{:able "Apple"
:baker "Berry"
:charlie "Kumquat"})
(defn manual-my-foo [item] (get foo item))
(defn manual-my-bar [item] (get bar item))
(manual-my-foo :able) => "Adelicious!"
(manual-my-bar :charlie) => "Kumquat"
因此manual my foo
是“硬连线”的,可以使用foo
全局地图
您可能认为需要一个宏来创建此函数,这是一种解决方案。但是,宏的一个缺点是它们不能作为参数传递给另一个函数,如map
。因此,我们可以编写如下宏:
(generate-fn :foo) ;=> creates `my-foo` w/o hand-writing it
但以下措施将失败:
(map generate-fn [:foo :bar :baz])
我们如何自动生成这些函数?虽然不能对宏使用
map
,但可以编写第二个宏来执行此函数。反过来,这可能需要编写第三个宏等,这是短语“宏一直向下”的来源,如和其他地方所述
通过使用Clojure的intern
函数,可以得到一个类似的问题。我们的问题与那个问题稍有不同,因为这里我们用两种不同的方式使用intern
:
- 使用
或def
defn
- 使用
var-get
spy
和spy let
。在您的项目中需要以下内容。clj
:
[tupelo "0.9.38"]
这允许我们编写以下代码:
(ns tst.clj.core
(:use clj.core )
(:require [tupelo.core :as t] ))
(t/refer-tupelo)
(def foo
{:able "Adelicious!"
:baker "Barbrallicious!"
:charlie "Charlizable"})
(def bar
{:able "Apple"
:baker "Berry"
:charlie "Kumquat"})
(defn generate-event-fn
[event-kw]
(spy-let [
event-str (name event-kw)
event-sym (symbol event-str)
fn-name (symbol (str "my-" event-str))
new-fn (fn fn-name [item]
(let [the-var (intern 'tst.clj.core event-sym) ; get the var the symbol 'event-sym' points to
the-map (var-get the-var) ; get the map the var is pointing to
the-str (get the-map item)] ; get the string from the map
(spyx the-var)
(spyx the-map)
(spyx the-str)))
]
(intern 'tst.clj.core fn-name new-fn) ; create a var 'fn-name' pointing to 'new-fn'
))
(newline) (println "*** generating functions ***")
(newline) (generate-event-fn :foo) ; creates and interns a function 'my-foo'
(newline) (generate-event-fn :bar) ; creates and interns a function 'my-bar'
(newline) (println "*** calling function ***")
(newline) (spyx (my-foo :able))
(newline) (spyx (my-bar :charlie))
执行时,我们会看到以下结果:
*** generating functions ***
event-str => "foo"
event-sym => foo
fn-name => my-foo
new-fn => #object[tst.clj.core$generate_event_fn$fn_name__32477 0x1ebd2a1b "tst.clj.core$generate_event_fn$fn_name__32477@1ebd2a1b"]
event-str => "bar"
event-sym => bar
fn-name => my-bar
new-fn => #object[tst.clj.core$generate_event_fn$fn_name__32477 0x4824083 "tst.clj.core$generate_event_fn$fn_name__32477@4824083"]
*** calling function ***
the-var => #'tst.clj.core/foo
the-map => {:able "Adelicious!", :baker "Barbrallicious!", :charlie "Charlizable"}
the-str => "Adelicious!"
(my-foo :able) => "Adelicious!"
the-var => #'tst.clj.core/bar
the-map => {:able "Apple", :baker "Berry", :charlie "Kumquat"}
the-str => "Kumquat"
(my-bar :charlie) => "Kumquat"
因此,我们创建了函数my foo
和my bar
,分别可以访问全局foo
和bar
地图。我们不需要使用宏
在Clojure中,var在某种程度上是介于foo
或my foo
等符号和它们所指向的值(本例中分别是一个映射和一个函数)之间的一个看不见的“中间人”。有关更多信息,请参阅相关帖子:
我们如何自动生成这些函数
你不需要。foo
和bar
贴图可按您所需的功能操作:
(foo :able) ; "Adelicious!"
(bar :able) ; "Apple"
仅供参考,在Stackoverflow中推广您自己的库(除非确实需要答案)是不受欢迎的:(,)我本可以使用
println
,而不是spy
&spy let
,但这会增加重复和混乱。你认为println
会更好吗?我只是看不到在原始问题中有人问“如何调试/打印中间值”。如果有一天出现调试问题,您可以提出tupelo库。否则,请回答问题而不使用调试代码。而FWIW,一个简单的内联(def xx the val)
是我在调试时所做的一切,因为数据>打印字符串
这是对另一个问题的回应:用户在哪里尝试自动生成回调函数。我只是试图简化成一个更通用的函数。也许我在这里过于简单了。我可能错了,但我真的不认为最初的动机是为地图中的关键字编写访问器——这只是一个过于简单的例子。当然,在这个例子中,复杂的解决方案是不必要的,但我不想用10段的篇幅来解释复杂性,因为我问了一个简单的问题。这最初是因为这个问题,用户试图在CLJS中自动生成一些回调函数:我认为使用intern
而不是宏调用宏是一种错误很好的解决方案。