在 clojure 上保存状态
Save state on clojure
我现在开始学习函数式编程,我对没有变量的工作感到非常着迷。
我读过的每个教程都说重新定义变量并不酷,但我不知道如何在不保存变量状态的情况下解决我的实际问题。
例如:我正在处理 API,我想在整个请求中保留这些值。假设我有一个添加 person
的端点,并且我有一个 persons
的列表,我想 redefine
或更改我的 persons
的值列表添加新的 person
。我该怎么做?
可以使用var-set
、alter-var-root
或conj!
吗?
(对于 api,我使用 compojure-api
,每个 person
将是一个 Hash
)
您可能需要在大型应用程序的某处使用可变状态,但并非在所有情况下都需要。
我不熟悉 compojure,但这里有一个使用不可变性的小例子,可能会给你一个更好的主意:
(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
更新它们。这是 "redefine a variable" 的简单方法。这与纯递归相当,您在任何时候都不会改变最终结果,您只需更改传递给下一次迭代的值。
当然,这非常简单,而且不切实际,因为您一次只能收到一个请求。如果您同时接收来自多个线程的请求,这将是一个原子的合理情况:
(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 with
swap!
通过 conj
加入它持有的列表来改变原子。原子内部的列表没有发生变化,它只是被自身的修改版本所取代。 持有对旧人员列表的引用的任何人都不会受到调用 swap!
的影响。
更好的方法是使用像 core/async 这样的库,但这样就偏离了问题。
关键是,您可能需要在某处使用可变变量,但对它们的需求比您习惯的要少得多。在大多数情况下,几乎所有事情都可以像第一个示例一样使用不变性来完成。
Clojure 将值与身份区分开来。您可以使用 atoms 来管理 compojure 应用程序中的状态。
(def persons (atom [])) ;; init persons as empty vector
(swap! persons #(conj % {:name "John Doe"})) ;; append new value
您可以在文档中找到更多信息:
https://clojure.org/reference/atoms
我现在开始学习函数式编程,我对没有变量的工作感到非常着迷。
我读过的每个教程都说重新定义变量并不酷,但我不知道如何在不保存变量状态的情况下解决我的实际问题。
例如:我正在处理 API,我想在整个请求中保留这些值。假设我有一个添加 person
的端点,并且我有一个 persons
的列表,我想 redefine
或更改我的 persons
的值列表添加新的 person
。我该怎么做?
可以使用var-set
、alter-var-root
或conj!
吗?
(对于 api,我使用 compojure-api
,每个 person
将是一个 Hash
)
您可能需要在大型应用程序的某处使用可变状态,但并非在所有情况下都需要。
我不熟悉 compojure,但这里有一个使用不可变性的小例子,可能会给你一个更好的主意:
(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
更新它们。这是 "redefine a variable" 的简单方法。这与纯递归相当,您在任何时候都不会改变最终结果,您只需更改传递给下一次迭代的值。
当然,这非常简单,而且不切实际,因为您一次只能收到一个请求。如果您同时接收来自多个线程的请求,这将是一个原子的合理情况:
(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 with
swap!
通过 conj
加入它持有的列表来改变原子。原子内部的列表没有发生变化,它只是被自身的修改版本所取代。 持有对旧人员列表的引用的任何人都不会受到调用 swap!
的影响。
更好的方法是使用像 core/async 这样的库,但这样就偏离了问题。
关键是,您可能需要在某处使用可变变量,但对它们的需求比您习惯的要少得多。在大多数情况下,几乎所有事情都可以像第一个示例一样使用不变性来完成。
Clojure 将值与身份区分开来。您可以使用 atoms 来管理 compojure 应用程序中的状态。
(def persons (atom [])) ;; init persons as empty vector
(swap! persons #(conj % {:name "John Doe"})) ;; append new value
您可以在文档中找到更多信息:
https://clojure.org/reference/atoms