生成名称的愿望,意味着你将会通过一个单一的地图,而不是得到更好的服务:
(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)控制的一个或几个变量中。我鼓励你想想你的问题是否可以用这种方式用单个原子来解决,而不是要求定义多个变量。尽管如此,如果您真的需要多个原子,请考虑将它们全部存储在一个地图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
[email protected](for [sym (for [i (range n)]
(symbol (str "neig" i)))]
`(def ~sym (atom {}))))
在我看来,这是不是一个功能版本的攻势居然少,不仅因为它是创造全球DEFS。通过使用常规的def
语法来创建全局视图更为明显。但我只是把它作为一个稻草人来提出来,因为这仍然很糟糕。
功能和数据效果最好的原因是因为它们组成。
有一些有形的考虑因素使得单个原子控制状态非常方便。您可以方便地遍历所有邻居,您可以动态添加新邻居。你也可以做一些事情,如连接邻居和其他邻居等。基本上有很多功能/数据抽象,如果你创建了许多全局变量,你就会锁定自己。
这就是宏一般认为对语法技巧有用的原因,但最好避免使用函数和数据。它对代码的灵活性有着实际的影响。例如回到组合;宏语法实际上是非常有限的,因此我宁愿不使用defroutes
。
总结:
- 不要让许多全球DEFS的,如果你能避免它。
- 在可能的情况下,优先选择多个原子上的1个原子。
- 不要在函数内部定义。
- 最好避免使用宏来支持函数和数据。
- 无论这些指导方针如何,探索什么是可能的,并且我无法了解您的情况总是很好的,所以我希望您最终克服了您的直接问题,并找到了一种令人愉快的语言来使用Clojure。
但是,你的功能的实际问题是什么?你在当前命名空间中生成符号,所以你只需要用'atom'调用来包装你的向量。 – leetwinski