用于函数重置的 clojure

Posted

技术标签:

【中文标题】用于函数重置的 clojure【英文标题】:clojure for function resets let 【发布时间】:2018-06-04 00:30:28 【问题描述】:

我正在尝试解决4clojure.com 上的一个问题,我应该在不使用count 的情况下计算集合中元素的数量。我尝试了两种使用forlet 的方法,我觉得它们应该可以工作,但似乎for-loop 一直在重置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)

所以我的问题是为什么会发生这种情况,以及如何让我的“变量”为集合中的每个项目保持递增。只是说变量这个词就让我想知道我是否正在尝试制作一些不能可变的东西,但我仍然觉得这应该可行。

【问题讨论】:

在学习 clojure 时要记住的一件事是 for 不是一个循环结构——它是一个列表推导式,用于创建列表。所以,当你想真正循环时,通常最好使用loop。回顾这个,我使用了reduce,除了纯递归之外,它是解决问题的最“函数式编程”方式(我的意思是,比显式循环更重要)。干杯,玩得开心! 【参考方案1】:

在这两种情况下,您都不能更改 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 %))

【讨论】:

惊人的答案!我发现reduceloop/recur 的使用最容易在心理上应用到这个特定的问题,但其他示例确实有助于更好地理解如何以不同的方式看待问题。 @Thumbnail :你是什么意思?答案中有这个确切的变体(查看count-reduce @leetwinski 错过了。

以上是关于用于函数重置的 clojure的主要内容,如果未能解决你的问题,请参考以下文章

Clojure 入门 学习笔记

解析用户密码重置 Swift iOS

Clojure学习笔记——介绍安装和语法

什么时候应该使用交换或重置

formGroupDirective 用于重置表单 - Angular 反应式表单

在javascript中为什么重置后函数的原型也需要重置函数原型构造函数?