首先,我没有使用CS的经验,Clojure是我的第一语言,所以如果下面的问题有一个解决方案,请原谅,这对程序员来说是显而易见的。
这个问题的总结如下:一个人需要在未知的时间任意地用未知的符号创造原子。我的方法围绕着( a)将原子的名称暂时存储在原子本身中;( b)将这些字符串更改为带有函数的符号;( c)使用函数添加和创建新的原子。问题与步骤“c”有关:调用函数并不会创建新的原子,但是使用它的主体可以创建它们。
在REPL中采取的所有步骤如下(代码块后面有注释):
user=> (def atom-pool
#_=> (atom ["a1" "a2"]))
#'user/atom-pool原子池是将中间原子作为字符串存储的原子.
user=> (defn atom-symbols []
#_=> (mapv symbol (deref atom-pool)))
#'user/atom-symbols
user=> (defmacro populate-atoms []
#_=> (let [qs (vec (remove #(resolve %) (atom-symbols)))]
#_=> `(do ~@(for [s qs]
#_=> `(def ~s (atom #{}))))))
#'user/populate-atoms“填充原子是定义原子的宏。注意,(remove #(resolve %) (atom-symbols))的目的只是创建尚未存在的原子。”原子符号读取“原子池”,并将其内容转化为符号。
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(nil nil nil)这里证实,目前还没有'a1','a2',‘a-新’原子。
user=> (defn new-atom [a]
#_=> (do
#_=> (swap! atom-pool conj a)
#_=> (populate-atoms)))
#'user/new-atom“新原子是函数,它首先将新的准原子作为字符串添加到”原子池“中,然后填充原子从原子符号函数中创建所有的原子。
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 nil)这里我们看到“a1”a2是通过定义一个函数作为clojure.lang.Var$Unbound创建的,为什么?
user=> (new-atom "a-new")
#'user/a2
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 nil)调用(new-atom "a-new")并没有创建‘一个新的原子!
user=> (do
#_=> (swap! atom-pool conj "a-new")
#_=> (populate-atoms))
#'user/a-new
user=> (for [s ['a1 'a2 'a-new]]
#_=> (resolve s))
(#'user/a1 #'user/a2 #'user/a-new)
user=> 在这里我们看到,显式地求助于“新原子的主体”确实创建了‘a-新原子’,a-new是clojure.lang.Atom的一种类型,但由于在名称空间中已经以clojure.lang.Var$Unbound的形式出现,所以跳过了'a1‘和'a2’。
感谢任何帮助如何使它工作!
编辑:注意,这是一个例子。在我的项目中,“原子池”实际上是映射的集合(带映射的原子)。这些映射有键{:name val}。如果添加了一个新的映射,那么通过解析它的:name键,为这个映射创建一个对应的原子。
发布于 2014-06-12 16:05:34
@JoostDiepenmaat关于为什么populate-atoms没有像预期的那样行事是正确的。您不能使用宏来完成这个任务,通常最好避免在运行时生成vars。更好的解决方案是将atom-pool定义为原子的关键字映射:
(def atom-pool
(atom {:a1 (atom #{}) :a2 (atom #{})}))然后您不需要atom-symbols或populate-atoms,因为您不是在编译时处理vars,而是在运行时处理典型的数据结构。您的新原子函数可能如下所示:
(defn new-atom [kw]
(swap! atom-pool assoc kw (atom #{})))编辑:如果不希望new-atom函数覆盖可能包含实际数据的现有原子,而不仅仅是#{},则可以首先检查原子是否存在于atom-pool中。
(defn new-atom [kw]
(when-not (kw @atom-pool)
(swap! atom-pool assoc kw (atom #{}))))发布于 2014-06-12 15:20:06
“这个问题的总结如下:一个人需要在未知的时间随意地用未知的符号创造原子。”
这听起来像是一个寻找问题的解决方案。我通常建议您尝试另一种方法来实现实际的功能,而不需要在运行时生成vars,但是如果必须的话,应该使用intern并删除宏内容。
您不能用宏解决这个问题,因为宏是在编译时展开的,这意味着
(defn new-atom [a]
(do
(swap! atom-pool conj a)
(populate-atoms)))populate-atoms只展开一次;当编译(defn new-atom ...)表单时,但是在调用new-atom时尝试更改它的扩展(这在以后必然会发生)。
发布于 2014-06-12 17:17:49
我已经为这个问题提交了一个答案,我认为这个答案更好,但是这里有一个基于eval的完全不同的方法
(def atom-pool (atom ["a1" "a2"]))
(defn new-atom! [name]
(load-string (format "(def %s (atom #{}))" name)))
(defn populate-atoms! []
(doseq [x atom-pool]
(new-atom x)))format构建一个字符串,其中%s被您要传入的名称替换。load-string将结果字符串(def "name" (atom #{}))读入为数据结构,并将其作为eval(这相当于(eval (read-string "(def ...)。
当然,那我们就只能定义不存在的原子了。我们可以修改我们的new-atom!函数,使之成为只在它还不存在的情况下创建一个原子:
(defn new-atom! [name]
(when-not (resolve (symbol name))
(load-string (format "(def %s (atom #{}))" name name))))在大多数情况下,Clojure社区似乎反对使用eval,因为它通常是不需要的(在99%的情况下,宏或函数可以做您想做的事情*),而且eval可能是潜在的不安全的,特别是在涉及用户输入的情况下--参见Brian对this question的回答。
*在尝试使用宏解决这个特殊问题后,我得出结论,要么不依赖eval就可以完成它,要么我的宏编写技能就不足以完成宏的工作!
无论如何,我仍然认为我的另一个答案是一个更好的解决方案--通常,当您深入到编写宏或使用eval的具体细节时,可能有一种更简单的方法不涉及元编程。
https://stackoverflow.com/questions/24187225
复制相似问题