有没有更好的方法在 Clojure 中将事物构建为原子?
Is there a better approach to building things up as atoms in Clojure?
为了构建数据结构,我发现自己做了很多事情,例如:
(let [foo (atom [])]
(do
(swap! foo conj {:foo "bar"})
(swap! foo conj {:foo "baz"}))
@foo)
=> [{:foo "bar"} {:foo "baz"}]
这是反模式吗?我用了很多原子。
首先,您不需要 do
,因为它隐含在 let
中。然后,对于这个例子,普通的旧 ->
效果很好(使用 my favorite template project):
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn stuff
[]
(-> []
(conj {:foo "bar"})
(conj {:foo "baz"})))
(dotest
(is= (stuff)
[{:foo "bar"}
{:foo "baz"}])
另一个选项是用户 reduce
:
(defn save-odds
[accum arg]
(if (odd? arg)
(conj accum arg)
accum))
<snip>
(is= (reduce save-odds
[]
(range 6))
[1 3 5]))
话虽如此,恕我直言,使用原子作为累加器并没有错。它简单明了。而且,如果原子的“令人讨厌的”突变永远不会泄漏到您的函数之外,它就不会在程序的其余部分造成任何复杂性。
"If mutation occurs and no outside function is affected, does
it really matter?"
毕竟reduce
和小伙伴们内部也是用mutation的,都是“函数式”编程的优秀范例。也就是说,它们是纯函数(具有引用透明性),并且不会产生副作用。
这里不需要atom
。您可以使用不可变数据结构:
(-> []
(conj {:foo "bar"})
(conj {:foo "baz"}))
;;=> [{:foo "bar"} {:foo "baz"}]
对于来自 OOP 或命令式语言的人来说,这可能是最困难的转变:避免可变性。
为了构建数据结构,我发现自己做了很多事情,例如:
(let [foo (atom [])]
(do
(swap! foo conj {:foo "bar"})
(swap! foo conj {:foo "baz"}))
@foo)
=> [{:foo "bar"} {:foo "baz"}]
这是反模式吗?我用了很多原子。
首先,您不需要 do
,因为它隐含在 let
中。然后,对于这个例子,普通的旧 ->
效果很好(使用 my favorite template project):
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn stuff
[]
(-> []
(conj {:foo "bar"})
(conj {:foo "baz"})))
(dotest
(is= (stuff)
[{:foo "bar"}
{:foo "baz"}])
另一个选项是用户 reduce
:
(defn save-odds
[accum arg]
(if (odd? arg)
(conj accum arg)
accum))
<snip>
(is= (reduce save-odds
[]
(range 6))
[1 3 5]))
话虽如此,恕我直言,使用原子作为累加器并没有错。它简单明了。而且,如果原子的“令人讨厌的”突变永远不会泄漏到您的函数之外,它就不会在程序的其余部分造成任何复杂性。
"If mutation occurs and no outside function is affected, does it really matter?"
毕竟reduce
和小伙伴们内部也是用mutation的,都是“函数式”编程的优秀范例。也就是说,它们是纯函数(具有引用透明性),并且不会产生副作用。
这里不需要atom
。您可以使用不可变数据结构:
(-> []
(conj {:foo "bar"})
(conj {:foo "baz"}))
;;=> [{:foo "bar"} {:foo "baz"}]
对于来自 OOP 或命令式语言的人来说,这可能是最困难的转变:避免可变性。