首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么在这个例子中调用(f arg)和调用f的主体显式地产生不同的结果?

为什么在这个例子中调用(f arg)和调用f的主体显式地产生不同的结果?
EN

Stack Overflow用户
提问于 2014-06-12 14:39:27
回答 3查看 143关注 0票数 1

首先,我没有使用CS的经验,Clojure是我的第一语言,所以如果下面的问题有一个解决方案,请原谅,这对程序员来说是显而易见的。

这个问题的总结如下:一个人需要在未知的时间任意地用未知的符号创造原子。我的方法围绕着( a)将原子的名称暂时存储在原子本身中;( b)将这些字符串更改为带有函数的符号;( c)使用函数添加和创建新的原子。问题与步骤“c”有关:调用函数并不会创建新的原子,但是使用它的主体可以创建它们。

在REPL中采取的所有步骤如下(代码块后面有注释):

代码语言:javascript
复制
user=> (def atom-pool
  #_=>   (atom ["a1" "a2"]))
#'user/atom-pool

原子池是将中间原子作为字符串存储的原子.

代码语言:javascript
复制
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))的目的只是创建尚未存在的原子。”原子符号读取“原子池”,并将其内容转化为符号。

代码语言:javascript
复制
user=> (for [s ['a1 'a2 'a-new]]
  #_=>   (resolve s))
(nil nil nil)

这里证实,目前还没有'a1','a2',‘a-新’原子。

代码语言:javascript
复制
user=> (defn new-atom [a]
  #_=>   (do
  #_=>     (swap! atom-pool conj a)
  #_=>     (populate-atoms)))
#'user/new-atom

“新原子是函数,它首先将新的准原子作为字符串添加到”原子池“中,然后填充原子从原子符号函数中创建所有的原子。

代码语言:javascript
复制
user=> (for [s ['a1 'a2 'a-new]]
  #_=>   (resolve s))
(#'user/a1 #'user/a2 nil)

这里我们看到“a1”a2是通过定义一个函数作为clojure.lang.Var$Unbound创建的,为什么?

代码语言:javascript
复制
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")并没有创建‘一个新的原子!

代码语言:javascript
复制
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键,为这个映射创建一个对应的原子。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-06-12 16:05:34

@JoostDiepenmaat关于为什么populate-atoms没有像预期的那样行事是正确的。您不能使用宏来完成这个任务,通常最好避免在运行时生成vars。更好的解决方案是将atom-pool定义为原子的关键字映射:

代码语言:javascript
复制
(def atom-pool
  (atom {:a1 (atom #{}) :a2 (atom #{})}))

然后您不需要atom-symbolspopulate-atoms,因为您不是在编译时处理vars,而是在运行时处理典型的数据结构。您的新原子函数可能如下所示:

代码语言:javascript
复制
(defn new-atom [kw]
  (swap! atom-pool assoc kw (atom #{})))

编辑:如果不希望new-atom函数覆盖可能包含实际数据的现有原子,而不仅仅是#{},则可以首先检查原子是否存在于atom-pool中。

代码语言:javascript
复制
(defn new-atom [kw]
  (when-not (kw @atom-pool)
    (swap! atom-pool assoc kw (atom #{}))))
票数 3
EN

Stack Overflow用户

发布于 2014-06-12 15:20:06

“这个问题的总结如下:一个人需要在未知的时间随意地用未知的符号创造原子。”

这听起来像是一个寻找问题的解决方案。我通常建议您尝试另一种方法来实现实际的功能,而不需要在运行时生成vars,但是如果必须的话,应该使用intern并删除宏内容。

您不能用宏解决这个问题,因为宏是在编译时展开的,这意味着

代码语言:javascript
复制
(defn new-atom [a]
 (do
   (swap! atom-pool conj a)
   (populate-atoms)))

populate-atoms只展开一次;当编译(defn new-atom ...)表单时,但是在调用new-atom时尝试更改它的扩展(这在以后必然会发生)。

票数 5
EN

Stack Overflow用户

发布于 2014-06-12 17:17:49

我已经为这个问题提交了一个答案,我认为这个答案更好,但是这里有一个基于eval的完全不同的方法

代码语言:javascript
复制
(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!函数,使之成为只在它还不存在的情况下创建一个原子:

代码语言:javascript
复制
(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的具体细节时,可能有一种更简单的方法不涉及元编程。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24187225

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档