我重写这篇文章是为了让原始问题更加清晰,并添加了全宏解决方案。请忽略此版本并参考以下内容:
How to create Clojure `defn` functions automatically without macros?
不幸的是,SO不允许我删除这个旧版本。
最初的动机是以下问题:Mapped calls to clojurescript macro
假设您想要自动创建许多类似的功能(即无需手动编写所有功能)。假设我们有一些预先存在的数据,我们想要编写这样的访问器函数:
(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])
我们如何自动生成这些功能?
答案 0 :(得分:2)
我们如何自动生成这些功能?
你不需要。 foo
和bar
地图作为您想要的功能运行:
(foo :able) ; "Adelicious!"
(bar :able) ; "Apple"
答案 1 :(得分:-4)
虽然您无法将map
与宏一起使用,但您可以编写第二个宏来执行此功能。反过来,这可能需要编写第三个宏等,这是Clojure for the Brave and True和其他地方所描述的短语“Macros All Way Down”的起源。
使用Clojure的intern
函数进行类似的问题was answered here。我们的问题与该问题略有不同,因为我们在这里以两种不同的方式使用intern
:
def
或defn
var-get
为减少输入,我们将使用spy
和spy-let
from the Tupelo library。您需要project.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
之类的符号之间的不可见“中间人”和他们指向的值(分别是地图和函数)例)。有关详细信息,请参阅相关帖子: