我正在尝试实现神经网络,每个神经元都需要引用其他神经元。我一直试图通过原子实现这个引用。
考虑代码
(def neuron1 {:connections [(atom 0)])
(def neuron2 {:connections [(atom neuron1)]})
(update-in neuron1 [:connections 0] #(reset! % neuron2))
最后一次会破坏筹码。
因此,似乎原子包含其内容,而不仅仅是引用它们。
如果我想传递等效的指针,我该怎么办?我是否必须使用功能,如
(def neuron1 {:connections [(fn [] neuron2)]})
并调用它,而不是使用原子并取消引用它?
答案 0 :(得分:5)
你的代码很好。你正在吹嘘堆栈的原因是你在REPL上运行update-in命令,导致它打印结果。由于每个神经元都嵌套在另一个神经元中,因此print语句遇到堆栈溢出。尝试将update-in命令包含在另一个语句中,例如(type),或者在另一个函数中运行它。
答案 1 :(得分:3)
WolfeFan已经回答了问题“为什么是stackoverflow”。 就指针/参考类型的特征而言,您可以将var(它与神经元绑定)存储在原子中,而不是神经元对象本身。此外,我建议将连接作为向量的原子而不是单个连接作为原子,因为大多数情况下你可能会从多个线程修改连接对象。
示例:
(def neuron1 {:connections (atom [])})
(def neuron2 {:connections (atom [#'neuron1])})
(update-in neuron1 [:connections] #(swap! % conj #'neuron2))
当您需要获取连接的神经元时,您需要使用var-get
:
(-> neuron1 :connections deref (get 0) var-get)
Vars本身是线程安全的,要更改var的根绑定,你需要使用alter-var-root
这是一个原子操作。
答案 2 :(得分:3)
如果你想使用简单的Clojure数据结构,最好不要忘记细粒度使用原子。相反,制作一个巨大的地图来代表整个网络,可能就像:
(def ann {1 {:connections [1 2 3] :weights [0.1 -0.3 0.5] :state 0.3} 2 {:connections [1 2 3] :weights [0.1 -0.3 0.5] :state 0.13} 3 {:connections [2 3] :weights [0.5 0.2] :state 0.31}})
然后使用update-in等来更新节点。使所有更新函数都采用不可变数据结构,因为这将使测试更容易。
现在肯定有比这更好的方法。你最好看看@mikera用矩阵做什么。
或者,如果你想要异步,你可以使用Lamina或新的Plumbing / Graph库。