【问题标题】:count-leaves in Clojure from SICP来自 SICP 的 Clojure 中的叶计数
【发布时间】:2017-01-07 09:37:46
【问题描述】:

我正在学习将问题转换为 Clojure 的 SICP,以学习 Clojure 和阅读 SICP。目前,我坚持使用第 2.2.2 节中的 Count Leaves 程序。

目标是编写一个函数,该函数采用树的列表表示,例如'(1 2 '(3 4)) 并计算叶子的数量,在本例中为 4。

到目前为止,我想出的最接近的是

(defn count-leaves
  [coll]
  (cond
    (nil? coll) 0
    (not (seq? coll)) 1
    :else (let [[left & right] coll] (+ (count-leaves left) (count-leaves right)))
    ))

但是,这不能正确处理子树。特别是,它评估

(count-leaves '('(1)))

改为 2 而不是 1。

注意 Scheme 实现 from the book 是:

(define (count-leaves x)
  (cond ((null? x) 0)
        ((not (pair? x)) 1)
        (else (+ (count-leaves (car x))
                 (count-leaves (cdr x))))))

【问题讨论】:

  • "...将 (count-leaves '('(1))) 评估为 2..." - 应该如此。 '('(1)) 的计算结果为 ((quote (1)))。不要在已经引用的列表中引用子列表。
  • 谢谢,我以为'('(1)) 和(list (list 1)) 一样,但显然我错了。更多信息:stackoverflow.com/questions/38515614/…

标签: clojure lisp sicp


【解决方案1】:

评论

正如@jkiski 的评论所暗示的,您的代码有效。所以没有问题。

但我更愿意先测试参数是否是一个序列。尝试计算 (count-leaves '()) 如何评估为 0

切换cond的前两个子句,我们得到...

(defn count-leaves [coll]
  (cond
    (not (seq? coll)) 1
    (empty? coll) 0
    :else (+ (count-leaves (first coll)) (count-leaves (rest coll)))))

... 我使用rest 而不是next 隐含在解构中,所以empty? 而不是nil? 来测试它。这可以正确处理 nil 值,而您的代码不能。但它仍然是正确的递归,所以仍然会受到堆栈溢出的影响。

我更喜欢……

(defn count-leaves [coll]
  (if (seq? coll)
    (apply + (map count-leaves coll))
    1))

...它仍然是递归的,但更简洁。


编辑

我不得不收回我对@glts's solution 的好看法:postwalk 是递归的,所以没有真正的优势。

【讨论】:

    【解决方案2】:

    将示例从一种语言翻译成另一种语言是一种很好的练习,但请记住,一种语言也有自己的习语和自己的核心库。

    在 Clojure 中,使用 clojure.walk 遍历数据结构特别容易。

    在要求clojure.walk 之后,你可以运行postwalk-demo 来查看你的数据结构是如何被遍历的:

    (require '[clojure.walk :refer [postwalk postwalk-demo]])
    (postwalk-demo '(1 2 (3 4)))
    
    Walked: 1
    Walked: 2
    Walked: 3
    Walked: 4
    Walked: (3 4)
    Walked: (1 2 (3 4))
    

    然后您可以设计一个函数来计算叶节点并将其传递给postwalk

    (postwalk (fn [e]
                (if (seq? e) (apply + e) 1))
              '(1 2 (3 4)))
    

    在 postwalk 遍历期间,叶节点被替换为 1,而 seqs 被替换为它们的组成叶计数之和。

    我意识到这是一个切题的答案,但也许你仍然觉得它有用!

    【讨论】:

      猜你喜欢
      • 2014-12-14
      • 1970-01-01
      • 1970-01-01
      • 2013-10-04
      • 1970-01-01
      • 2019-05-03
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      相关资源
      最近更新 更多