【问题标题】:Clojure - Using recursion to find the number of elements in a listClojure - 使用递归查找列表中的元素数
【发布时间】:2017-01-03 16:08:35
【问题描述】:

我编写了一个函数,它使用递归来查找列表中的元素数量,并且它可以成功运行,但是我并不特别喜欢我编写它的方式。现在我用一种我想不出的另一种方式来写它。

我的代码如下:

(def length 
 (fn [n]
  (loop [i n total 0]
   (cond (= 0 i) total
     :t (recur (rest i)(inc total)))))) 

在我看来,它似乎过于复杂,谁能想到另一种可以写成比较的方式?

非常感谢任何帮助。

【问题讨论】:

  • 为什么不直接使用clojuredocs.org/clojure.core/count
  • 我必须使用递归使它工作,这就是为什么我走上了使用循环的路线,以及为什么它最终变得如此复杂。递归使用 count 有意义吗?
  • 使用loop-recur 你只有一个循环,而不是递归。
  • loop/recur 在概念上与递归函数相同——这就是为什么“recur”是名称的一部分。它在 JVM 上作为循环实现,但如果您将其移植到方案中,您将使用递归函数来实现它。
  • @amalloy,我认为应该指出该方案的标准要求 TailCall-Optimization,所以如果我没有弄错你的“作为递归函数的实现”,很可能会以循环,因此与通过关键字的显式尾调用相同(如recur is)。 - 除了它是自动完成的

标签: list recursion clojure


【解决方案1】:

这是一个简单的递归版本:

(defn my-count [coll]
    (if (empty? coll) 
        0 
        (inc (my-count (rest coll)))))

请记住,这里不会进行任何尾调用优化,因此对于长列表,堆栈会溢出。

这是一个使用reduce的版本:

(defn my-count [coll]
    (reduce (fn [acc x] (inc acc)) 0 coll))

【讨论】:

    【解决方案2】:

    这是显示一些不同解决方案的代码。一般情况下,应该使用内置函数count

    (def data [:one :two :three])
    
    (defn count-loop [data]
      (loop [cnt 0
             remaining data]
        (if (empty? remaining)
          cnt
          (recur (inc cnt) (rest remaining)))))
    
    (defn count-recursive [remaining]
        (if (empty? remaining)
          0
          (inc (count-recursive (rest remaining)))))
    
    (defn count-imperative [data]
      (let [cnt (atom 0)]
        (doseq [elem data]
          (swap! cnt inc))
        @cnt))
    
    (deftest t-count
      (is (= 3 (count data)))
      (is (= 3 (count-loop data)))
      (is (= 3 (count-recursive data)))
      (is (= 3 (count-imperative data))))
    

    【讨论】:

    • 为什么还要提到如何用原子做到这一点?这是任何人都会反对的反模式;如果您想在其他两种好的方法中添加第三种方法,reduce 将是展示的功能。
    • 有时命令式/可变方法是最简单的答案(显然,对于这个例子,除了count 之外的一切都是多余的)。如果没人能看到的代码是命令式的或可变的,而且它不会泄漏,为什么要担心呢?如果你挖得足够深,一切都是可变的/势在必行的。只有当它泄漏出来时,它才会成为一个问题。
    • 这是一种非常面向图灵机的思维方式。或者你可以说,如果你挖得足够深(lambda演算),一切都是递归的。只有当你必须在现实生活中的现代物理处理器上实际实现它时,命令式实现细节才会泄露。
    【解决方案3】:

    这是一个尾调用优化的,不依赖于loop。与 Alan Thompson 的第一个基本相同,但内部功能是最好的。 (对我来说感觉更惯用了。):-)

    (defn my-count [sq]
     (letfn [(inner-count [c s]
              (if (empty? s) 
               c
               (recur (inc c) (rest s))))]
      (inner-count 0 sq)))
    

    【讨论】:

      【解决方案4】:

      为了完整起见,这里有另一个转折

      (defn my-count
        ([data]
         (my-count data 0))
        ([data counter]
         (if (empty? data)
           counter
           (recur (rest data) (inc counter)))))
      

      【讨论】:

        猜你喜欢
        • 2011-09-22
        • 2016-06-28
        • 1970-01-01
        • 2019-03-12
        • 2018-06-13
        • 2013-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多