我想1)用下面的函数创建一个符号列表;然后2)创建具有这些符号/名称的原子,以便可以从其他函数修改原子。这是生成符号/名称的函数:
(defn genVars [ dist ]
(let [ nms (map str (range dist)) neigs (map #(apply str "neig" %) nms) ]
(doseq [ v neigs ]
(intern *ns* (symbol v) [ ] ))
))
如果dist = 3,则创建3个符号,neig0,... neig2,每个符号用空向量绑定。如果可以在功能上创建具有这些符号的原子,以便可以从其他函数访问它们。任何帮助都非常感谢,即使有其他方法可以实现这一目标。
答案 0 :(得分:2)
您的功能似乎是正确的,只需使用intern
来调用atom
来电中的值。我也宁愿使用dotimes
。
user>
(defn gen-atoms [amount prefix]
(dotimes [i amount]
(intern *ns* (symbol (str prefix i)) (atom []))))
#'user/gen-atoms
user> (gen-atoms 2 "x")
nil
user> x0
#atom[[] 0x30f1a7b]
user> x1
#atom[[] 0x2149efef]
答案 1 :(得分:1)
生成名称的愿望表明,您可以通过单个地图更好地服务:
(def neighbours (atom (make-neighbours)))
make-neigbours
的定义可能如下所示:
(defn make-neighbours []
(into {} (for [i (range 10)]
[(str "neig" i) {:age i}])))
其他命名空间看起来像是使用类似的东西:
(get-in @data/neighbours ["neig0" :age])
惯用语Clojure倾向于避免创建许多命名的全局变量,而是倾向于将状态并置为由Clojure的并发原语(atom / ref / agent)管理的一个或几个变量。我鼓励您考虑用这种方式用单个原子解决问题,而不是要求定义多个变量。
话虽如此,如果你真的需要多个原子,可以考虑将它们全部存储在一个map var中,而不是创建许多全局变量。就个人而言,我从来没有遇到过创造许多原子比一个大原子更好的情况(所以我很想知道这个重要的情况)。
如果你真的需要很多变量,请注意在函数中定义变量实际上是坏样式(https://github.com/bbatsov/clojure-style-guide#dont-def-vars-inside-fns)。还有很好的理由!使用功能和数据的美妙之处在于功能的纯洁性。函数内部的def
特别令人讨厌,因为它不仅是一种副作用,而且是一种改变副作用的潜在执行流程。
当然,有一种方法可以实现它,正如另一个答案所指出的那样。
在定义超出def
和defn
的内容时,使用宏有很多优先权。例如来自compojure的defroutes
,来自Schema的defschema
,来自clojure.test的deftest
。一般来说,任何东西都是创建变量的便利形式。您可以使用宏解决方案为原子创建defs:
(defmacro defneighbours [n]
`(do
~@(for [sym (for [i (range n)]
(symbol (str "neig" i)))]
`(def ~sym (atom {}))))
在我看来,这实际上不如功能版本具有攻击性,只是因为它正在创建全局defs。通过使用常规def
语法创建全局defs更为明显。但我只是把它当作一个稻草人,因为这仍然很糟糕。
功能和数据最有效的原因在于它们的组成。
有一些切实的考虑使得单个原子控制状态非常方便。您可以方便地遍历所有邻居,可以动态添加新邻居。你也可以做一些事情,比如将邻居与其他邻居连接起来等等。基本上,如果你创建了许多全局变量,你可以锁定很多函数/数据抽象。
这就是宏通常被认为对语法技巧有用的原因,但最好避免使用函数和数据。它对代码的灵活性有实际影响。例如,回到compojure;宏语法实际上是非常有限的,因此我宁愿不使用defroutes
。
总结: