如何在ClojureScript中创建一个函数局部可变变量?

6
考虑下面这个假想的无意义ClojureScript函数:
```clojure (defn foo [x] (let [y (* x 2)] (if (> y 10) (+ y 1) (- y 1)))) ```
该函数接受一个参数x,并将其乘以2得到y。如果y大于10,则返回y加1,否则返回y减1。
(defn tmp [] 
  (def p 0)
  (set! p (inc p))
  (set! p (inc p))
  (set! p (inc p)))

在REPL中重复执行此函数会产生

3
6
9
etc.

是否可以创建一个局部函数可变变量,以便输出将是

3
3
3
etc. 

在重复执行(tmp)的情况下?
1个回答

8

let 使您可以为其作用域限定的变量赋值:

(defn tmp[]
  (let [p 0]
    ...))

现在,Clojurescript 使用不可变数据。这意味着一切都基本上是一个常量,一旦你设置了 p 的值,它就不能再改变了。有两种方法可以解决这个问题:

使用更多的局部变量

(defn tmp[]
  (let [a 0
        b (inc a)
        c (inc b)
        d (inc c)]
    ...))

使用atom

Atom在ClojureScript中与其他数据结构略有不同,允许控制其状态。基本上,您可以将它们视为对值的引用。

创建atom时,将初始值作为其参数传递。您可以通过在变量前面添加@来访问原子值,这实际上是(deref my-var)的宏。

您可以使用swap!reset!函数更改atom的值。在cljs cheatsheet中了解更多信息。

(defn tmp[]
  (let [p (atom 0)]
    (reset! p (inc @p))
    (reset! p (inc @p))
    (reset! p (inc @p))))

希望这能帮到你。

我当然尝试过,但是我得到了这个错误: clojure.lang.ExceptionInfo: Can't set! local var or non-mutable field at line 3 <cljs repl> - 我在标题中添加了“mutable”,但该术语出现在问题中。- 谁会点赞一个不正确的答案? - nilo de roock
1
我认为你可能已经意识到了,因为你将你的代码描述为部分无意义。无论如何,我更新了我的答案来涵盖所有内容。 - Kuba Birecki
谢谢,增加更多的本地变量在我手头的情况下行不通。一个本地原子会起到作用,我会立即尝试这个解决方案。 - nilo de roock
哇,真的!多么愚蠢的语言啊。本地原子(local atom)可以工作,但似乎有点奇怪。使用原子是一项昂贵的操作吗?在Clojure中实现这个String filename = null; try { possibly set filename } finally { if (filename != null) new File(filename).delete() }的等效操作相当困难。 - Nicholas DiPiazza
1
@NicholasDiPiazza 为什么你会“可能设置文件名”?通常在Clojure中有不使用可变变量(如String filename)的方法来完成任务。如果你真的需要一个,可以使用原子(atom)。原子(atom)的性能很好。它使用Java代码来管理共享、同步、独立的状态(例如,在处理并发代码时,你不必自己进行锁定)。 - user2609980

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接