1

我现在开始进行函数式编程,并且对没有变量的工作感到非常疯狂。

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

例如:我正在开发一个 API,我想通过请求保留这些值。假设我有一个添加 a 的端点person,并且我有一个 列表persons,我想redefine或更改persons添加新列表的值person。我怎样才能做到这一点?

有没有可以用var-setalter-var-root或者conj!

(对于我正在使用的 api compojure-api,每个person都是一个Hash

4

2 回答 2

6

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

(def persons (atom [])) ;; init persons as empty vector
(swap! persons #(conj % {:name "John Doe"})) ;; append new value

您可以在文档中找到更多信息:

https://clojure.org/reference/atoms

https://clojure.org/reference/data_structures

https://clojuredocs.org/clojure.core/atom

于 2018-03-25T22:45:16.400 回答
2

在大型应用程序的某个地方,您可能需要一个可变状态,但并非在所有情况下都需要这种状态。

我不熟悉 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-requestmake-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 with

swap!conj通过指向它持有的列表来改变原子。原子内部的列表没有发生突变,它只是被自身的修改版本替换。任何持有对旧人员列表的引用的人都不会受到对 的调用的影响swap!

更好的方法是使用像 core/async 这样的库,但这已经脱离了问题。

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

于 2018-03-25T22:44:43.883 回答