如何解决Clojure中的“有状态的问题”?

M.A*_*rkk 1 functional-programming clojure

我是Clojure的新手,我很难理解一些概念,特别是纯函数和不变性。

我仍然无法理解的一件事是如何在clojure中解决这样的问题:

一个具有登录方法的简单控制台应用程序,用户无法在1分钟的间隔内尝试登录3次以上。

例如,在C#中,我可以在用户每次尝试登录时将UserId和时间戳添加到集合中,然后检查最后一分钟是否有3个以上的尝试。

考虑到我无法更改自己的收藏,我将如何在Clojure中做到这一点?

这不是一个务实的问题(尽管会欢迎一些代码示例),但我想了解您如何解决这样的问题。

Car*_*ate 5

在大多数情况下,您不更改对象,而是创建旧对象的新版本:

(loop [attempt-dates []]
  (if (login-is-correct)
    (login)
    (recur (conj attempt-dates (current-date-stamp)))))
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我使用loop。无论我给什么,recur都会传递给的下一个迭代loop。我正在创建一个包含新标记的(conj attempt-dates (current-date-stamp))新列表,然后将其传递到的下一个迭代loop

在大多数情况下,就是这样做的。与其考虑更改对象,不如考虑创建对象的转换后的副本并传递该副本。

如果您确实确实需要可变状态,则可以使用可变atom状态来保存不可变状态:

(def mut-state (atom []))

(swap! mut-state conj 1)

(println @mut-state)  ; Prints [1]
Run Code Online (Sandbox Code Playgroud)

[]在这里仍然是不变的,新版本只是替换了可变atom容器中的旧版本。

除非您需要与UI回调或类似功能进行通信,否则通常通常不需要可变性。练习使用loop/ recurreduce代替。

  • @ M.Arkk哦,不。“尝试日期”在一次调用“循环”的每次迭代之间进行。如果您正在`loop`内接受请求,那么它将起作用。如果您正在其他地方异步处理请求,则可能会使用“ atom”。 (2认同)