clojure 函数重置让
clojure for function resets let
我正在尝试解决 4clojure.com 上的一个问题,我应该在不使用 count
的情况下计算集合中的元素数量。我尝试了两种使用 for
和 let
的方法,我觉得应该可行,但似乎 for 循环不断重置 let
.
(#(for [x % :let [y 0]] (inc y)) [1 2 3 4 5])
;; which returns
(1 1 1 1 1)
(#(let [y 0] (for [x %] (inc y))) [1 2 3 4 5])
;; which returns
(1 1 1 1 1)
所以我的问题是为什么会发生这种情况,以及我如何让我的 "variable" 为集合中的每个项目保持递增。只是说变量这个词让我想知道我是不是想做一些不能改变的东西,但我仍然觉得这应该有效。
在这两种情况下,您都无法更改 y
:
的值
在第一种情况下 for
在每个循环步骤中重新引入 y
,这就是为什么你不能更改它的值,即使它是可变的。
第二种情况表明该值确实是不可变的,每一步都为您提供 (inc y)
,但 y
始终为零。简单示例:
(let [x 10]
(inc x)
x)
;;=> 10
一般来说,这种任务通常可以通过以下方法解决:
第一个是简单的递归:
(defn count-rec [data]
(if (seq data)
(inc (count-rec (rest data)))
0))
user> (count-rec [1 2 3 4 5])
;;=> 5
它在某种程度上是有缺陷的,因为它不是尾递归的,并且对于大型集合会失败
第二个是clojure的loop
:
(defn count-loop [data]
(loop [res 0 data data]
(if (seq data)
(recur (inc res) (rest data))
res)))
user> (count-loop [1 2 3 4 5])
;;=> 5
您也可以使用非常相似的显式尾递归:
(defn count-tailrec
([data] (count-tailrec 0 data))
([c data] (if (seq data)
(recur (inc c) (rest data))
c)))
user> (count-tailrec [1 2 3 4 5])
;;=> 5
第三个将使用 reduce
函数:
(defn count-reduce [data]
(reduce (fn [res _] (inc res)) 0 data))
user> (count-reduce [1 2 3 4 5])
;;=> 5
为了好玩,您也可以使用这种方式(我不建议这样做,因为与 reduce
相比,它有点矫枉过正):
(defn count-map [data]
(apply + (map (constantly 1) data)))
user> (count-map [1 2 3 4 5])
;;=> 5
您也可以使用 clojure 的可变原语,但它不是惯用的,应该尽可能避免:
(defn count-atom [data]
(let [c (atom 0)]
(run! (fn [_] (swap! c inc)) data)
@c))
user> (count-atom [1 2 3 4 5])
;;=> 5
作弊来了[剧透警告!]
4clojure 在此任务中阻止了 count
函数的使用,但没有阻止 java 集合的 .size
方法,因此可以通过以下方式解决
#(.size (seq %))
我正在尝试解决 4clojure.com 上的一个问题,我应该在不使用 count
的情况下计算集合中的元素数量。我尝试了两种使用 for
和 let
的方法,我觉得应该可行,但似乎 for 循环不断重置 let
.
(#(for [x % :let [y 0]] (inc y)) [1 2 3 4 5])
;; which returns
(1 1 1 1 1)
(#(let [y 0] (for [x %] (inc y))) [1 2 3 4 5])
;; which returns
(1 1 1 1 1)
所以我的问题是为什么会发生这种情况,以及我如何让我的 "variable" 为集合中的每个项目保持递增。只是说变量这个词让我想知道我是不是想做一些不能改变的东西,但我仍然觉得这应该有效。
在这两种情况下,您都无法更改 y
:
在第一种情况下 for
在每个循环步骤中重新引入 y
,这就是为什么你不能更改它的值,即使它是可变的。
第二种情况表明该值确实是不可变的,每一步都为您提供 (inc y)
,但 y
始终为零。简单示例:
(let [x 10]
(inc x)
x)
;;=> 10
一般来说,这种任务通常可以通过以下方法解决:
第一个是简单的递归:
(defn count-rec [data]
(if (seq data)
(inc (count-rec (rest data)))
0))
user> (count-rec [1 2 3 4 5])
;;=> 5
它在某种程度上是有缺陷的,因为它不是尾递归的,并且对于大型集合会失败
第二个是clojure的loop
:
(defn count-loop [data]
(loop [res 0 data data]
(if (seq data)
(recur (inc res) (rest data))
res)))
user> (count-loop [1 2 3 4 5])
;;=> 5
您也可以使用非常相似的显式尾递归:
(defn count-tailrec
([data] (count-tailrec 0 data))
([c data] (if (seq data)
(recur (inc c) (rest data))
c)))
user> (count-tailrec [1 2 3 4 5])
;;=> 5
第三个将使用 reduce
函数:
(defn count-reduce [data]
(reduce (fn [res _] (inc res)) 0 data))
user> (count-reduce [1 2 3 4 5])
;;=> 5
为了好玩,您也可以使用这种方式(我不建议这样做,因为与 reduce
相比,它有点矫枉过正):
(defn count-map [data]
(apply + (map (constantly 1) data)))
user> (count-map [1 2 3 4 5])
;;=> 5
您也可以使用 clojure 的可变原语,但它不是惯用的,应该尽可能避免:
(defn count-atom [data]
(let [c (atom 0)]
(run! (fn [_] (swap! c inc)) data)
@c))
user> (count-atom [1 2 3 4 5])
;;=> 5
作弊来了[剧透警告!]
4clojure 在此任务中阻止了
count
函数的使用,但没有阻止 java 集合的.size
方法,因此可以通过以下方式解决#(.size (seq %))