2

我知道我可以在 Common Lisp 中执行以下操作:

CL-USER> (let ((my-list nil))
       (dotimes (i 5)
         (setf my-list (cons i my-list)))
       my-list)
(4 3 2 1 0)

我如何在 Clojure 中做到这一点?特别是,我如何在 Clojure 中没有 setf 的情况下做到这一点?

4

7 回答 7

8

我个人对您在 Common Lisp 中所做的事情的翻译是 Clojurewise:

(into (list) (range 5))

这导致:

(4 3 2 1 0)

一点解释:

该函数into将所有元素连接到一个集合中,这里是一个新列表,使用(list)来自其他集合创建,这里是 range 0 .. 4conj每个数据结构的行为不同。对于列表,conj其行为如下cons:它将一个元素放在列表的开头并将其作为新列表返回。所以它的作用是:

(cons 4 (cons 3 (cons 2 (cons 1 (cons 0 (list))))))

这类似于您在 Common Lisp 中所做的事情。Clojure 的不同之处在于我们一直在返回新列表,而不是更改一个列表。只有在 Clojure 中真正需要时才使用突变。

当然你也可以马上得到这个列表,但这可能不是你想知道的:

(range 4 -1 -1)

或者

(reverse (range 5))

或者......我能想到的最短版本:

'(4 3 2 1 0)

;-)。

于 2011-12-17T11:32:02.107 回答
7

在 Clojure 中执行此操作的方法是不这样做:Clojure 讨厌可变状态(它是可用的,但不鼓励将它用于每件小事)。相反,请注意这个模式:你真的在计算(cons 4 (cons 3 (cons 2 (cons 1 (cons 0 nil))))). 这看起来很像减少(或折叠,如果你愿意的话)。因此,(reduce (fn [acc x] (cons x acc)) nil (range 5)),这会产生您正在寻找的答案。

于 2011-12-17T00:09:09.750 回答
3

为了线程安全,Clojure 禁止局部变量的变异,但即使没有变异,仍然可以编写循环。在循环的每次运行中,您都希望my-list具有不同的值,但这也可以通过递归来实现:

(let [step (fn [i my-list]
             (if (< i 5)
               my-list
               (recur (inc i) (cons i my-list))))]
  (step 0 nil))

Clojure 也有一种“只做循环”而不创建新函数的方法,即loop. 它看起来像一个let,但您也可以跳到其主体的开头,更新绑定,然后使用 再次运行主体recur

(loop [i 0
       my-list nil]
  (if (< i 5)
    my-list
    (recur (inc i) (cons i my-list))))

使用递归尾调用“更新”参数看起来与改变变量非常相似,但有一个重要区别:当您输入Clojure 代码时,my-list其含义始终. my-list如果嵌套函数关闭my-list并且循环继续到下一次迭代,则嵌套函数将始终看到my-list创建嵌套函数时的值。局部变量总是可以用它的值替换,而你在递归调用后得到的变量在某种意义上是一个不同的变量。

(Clojure 编译器执行优化,因此这个“新变量”不需要额外的空间:当需要记住一个变量时,它的值被复制,而当recur被调用时,旧变量被重用。)

于 2011-12-19T16:10:52.577 回答
2

为此,我将使用range手动设置步骤:

(range 4 (dec 0) -1) ; => (4 3 2 1 0)

dec用 1 减少结束步骤,因此我们得到值 0。

于 2011-12-17T12:05:20.187 回答
1
用户=>(范围 5)
(0 1 2 3 4)
用户=>(取 5(迭代公司 0))
(0 1 2 3 4)
用户=>(对于 [x [-1 0 1 2 3]]
         (inc x)) ; 只是为了弄清楚发生了什么
(0 1 2 3 4)

setf是状态突变。Clojure 对此有非常具体的意见,并在您需要时提供相应的工具。在上述情况下你没有。

于 2011-12-16T23:46:16.620 回答
1

这是我一直在寻找的模式:

(loop [result [] x 5]
  (if (zero? x)
    result
    (recur (conj result x) (dec x))))

我在Stuart Halloway 和 Aaron Bedra的Programming Clojure (Second Edition) 中找到了答案。

于 2011-12-21T20:57:43.547 回答
1
(let [my-list (atom ())]
  (dotimes [i 5]
    (reset! my-list (cons i @my-list)))
  @my-list)

(def ^:dynamic my-list nil);need ^:dynamic in clojure 1.3
(binding [my-list ()]
  (dotimes [i 5]
    (set! my-list (cons i my-list)))
  my-list)
于 2011-12-17T11:49:27.073 回答