在clojure上保存状态

在clojure上保存状态,clojure,compojure,ring,compojure-api,Clojure,Compojure,Ring,Compojure Api,我现在开始做函数式编程,我对没有变量的工作非常着迷 我读过的每个教程都说重新定义变量并不酷,但我不知道如何在不保存变量状态的情况下解决实际问题 例如:我正在处理一个API,我希望在整个请求中保留这些值。假设我有一个端点,添加了一个人,我有一个人的列表,我想重新定义,或者更改我的人列表的值,添加新的人。我该怎么做 是否可以使用var set、alter var root或conj (对于我正在使用的api,compojure api,每个人都是一个散列)在大型应用程序中,您可能需要一个可变状态,但

我现在开始做函数式编程,我对没有变量的工作非常着迷

我读过的每个教程都说重新定义变量并不酷,但我不知道如何在不保存变量状态的情况下解决实际问题

例如:我正在处理一个API,我希望在整个请求中保留这些值。假设我有一个端点,添加了一个
,我有一个
人的列表
,我想
重新定义
,或者更改我的
列表的值,添加新的
。我该怎么做

是否可以使用
var set
alter var root
conj


(对于我正在使用的api,
compojure api
,每个
都是一个
散列

在大型应用程序中,您可能需要一个可变状态,但并非在所有情况下都需要

我不熟悉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))))])
我在这里使用假设的
receiverequest
makeperson
函数

循环
创建一对绑定,并在每次
重复时更新它们。这是“重新定义变量”的简单方法。这与纯递归相当,在纯递归中,任何时候都不改变最终结果,只需更改传递给下一次迭代的值

当然,这非常简单,而且不切实际,因为您一次只收到一个请求。如果您同时接收来自多个线程的请求,那么对于atom来说,这是一个合理的情况:

(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的影响

一个更好的方法是使用像core/async这样的库,但这样就避开了这个问题


关键是,您可能需要在某个地方使用可变变量,但对它们的需求要比您习惯的少很多。在大多数情况下,几乎所有的事情都可以使用不变性来完成,就像第一个例子一样

在大型应用程序中可能需要一个可变状态,但并非所有情况下都需要

我不熟悉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))))])
我在这里使用假设的
receiverequest
makeperson
函数

循环
创建一对绑定,并在每次
重复时更新它们。这是“重新定义变量”的简单方法。这与纯递归相当,在纯递归中,任何时候都不改变最终结果,只需更改传递给下一次迭代的值

当然,这非常简单,而且不切实际,因为您一次只收到一个请求。如果您同时接收来自多个线程的请求,那么对于atom来说,这是一个合理的情况:

(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的影响

一个更好的方法是使用像core/async这样的库,但这样就避开了这个问题


关键是,您可能需要在某个地方使用可变变量,但对它们的需求要比您习惯的少很多。在大多数情况下,几乎所有的事情都可以使用不变性来完成,就像第一个例子一样

Clojure将价值观与身份区分开来。您可以使用原子来管理compojure应用程序中的状态

(def persons (atom [])) ;; init persons as empty vector
(swap! persons #(conj % {:name "John Doe"})) ;; append new value
您可以在文档中找到更多信息:


Clojure将价值观与身份区分开来。您可以使用原子来管理compojure应用程序中的状态

(def persons (atom [])) ;; init persons as empty vector
(swap! persons #(conj % {:name "John Doe"})) ;; append new value
您可以在文档中找到更多信息:

请注意,原子交换值(因此称为“swap!”),此操作与设置变量不同。您可以使用reset!“设置”该值!,但更好的用法是通过对其应用函数来更改值,如上面的示例所示。原子不受种族限制,所以它们可以安全地同时使用。一开始,代码中有很多原子是很正常的,但最终您会非常少地使用它们,并且大多数代码只是通过函数传递值。请注意,原子交换值(因此称为“swap!”),此操作与设置变量不太像。您可以使用reset!“设置”该值!,但更好的用法是通过对其应用函数来更改值,如上面的示例所示。原子不受种族限制,所以它们可以安全地同时使用。最初,代码中有很多原子是很正常的,但最终您将非常少地使用它们,并且大多数代码只是通过函数传递值。