我现在开始进行函数式编程,我非常喜欢没有变量的工作。
我读过的每一篇教程都说重新定义变量并不酷,但我不知道如何解决实际问题而不将状态保存在变量上。
例如:我正在处理一个API,我希望通过请求保持这些值。假设我有一个添加person的终结点,并且我有一个persons列表,我想要redefine或更改添加新person的persons列表的值。我该怎么做?
可以使用var-set、alter-var-root或conj!吗?
(对于api,我使用的是compojure-api,每个person都是一个Hash)
发布于 2018-03-25 22:45:16
Clojure将值与标识区分开来。您可以在您的组合应用程序中使用原子来管理状态。
(def persons (atom [])) ;; init persons as empty vector
(swap! persons #(conj % {:name "John Doe"})) ;; append new value您可以在文档中找到更多:
发布于 2018-03-25 22:44:43
在大型应用程序中,您可能需要一个可变的状态,但并不是所有情况下都需要这样的状态。
我不太熟悉概念,但这里有一个使用不变的小例子,它可能会给您一个更好的想法:
(loop [requests []
people []
(let [request (receive-request)]
; Use requests/people
; Then loop again with updated lists
(recur (conj requests request)
(conj people (make-person request))))])这里我使用的是假设的receive-request和make-person函数。
loop创建几个绑定,并在每个recur上更新它们。这是一种“重新定义变量”的简单方法。这与纯递归类似,在这种情况下,您不会在任何时候更改最终结果,只需更改传递给下一次迭代的值。
当然,这非常简单,而且不切实际,因为您一次只收到一个请求。如果同时接收来自多个线程的请求,对于一个原子来说,这是一个合理的案例:
(defn listen [result-atom]
(Thread.
(fn []
(while true ; Infinite listener for simplicity
(let [request (receive-request)]
(swap! result-atom #(conj % (make-person request))))))))
(defn listen-all []
(let [result-atom (atom [])]
(listen result-atom)
(listen result-atom)))
; result-atom now holds an updating list of people that you can do stuff withswap!通过将原子插入到它所保存的列表上而使原子发生突变。原子内部的列表没有发生变异,它只是被修改过的自身版本所取代。任何保持对旧列表的引用的人都不会受到对swap!的调用的影响。
一种更好的方法是使用类似于core/异步的库,但这是在回避这个问题。
关键是,您可能需要在某个地方使用可变变量,但是对它们的需求要比以前少得多。在大多数情况下,几乎所有的事情都可以使用不变性来完成,就像在第一个例子中一样。
https://stackoverflow.com/questions/49481260
复制相似问题