我目前正在尝试学习Clojure。但是我在创建一个递归搜索列表中每个元素并返回列表中“ a”的数量的函数时遇到了麻烦。
我已经想出了如何迭代地执行它,但是我在递归地执行它时遇到了麻烦。我尝试将“ seq”更改为“ empty?”但这也不起作用。
(defn recursive-a [& lst]
(if (seq lst)
(if (= (first lst) "a")
(+ 1 (recursive-a (pop (lst))))
(+ 0 (recursive-a (pop (lst)))))
0))
答案 0 :(得分:1)
欢迎使用堆栈溢出社区。 p>
您的代码很好,只是您犯了一些小错误。
首先,在lst
参数周围还有一对括号,您可以将它们转发到递归函数。在LISP语言中,花括号表示功能评估。因此,首先您应该删除那些。
第二件事是&
参数语法糖。在确定它如何影响代码之前,您不希望使用它。
进行这些更改后,代码如下:
(defn recursive-a [lst]
(if (seq lst)
(if (= (first lst) "a")
(+ 1 (recursive-a (pop lst)))
(+ 0 (recursive-a (pop lst))))
0))
(recursive-a (list "a" "b" "c"))
您可以在网络环境中运行它:https://repl.it/languages/clojure
答案 1 :(得分:0)
欢迎堆栈溢出。
通过显式调用recursive-a
,原始实现在每次递归时都会消耗堆栈。如果提供了足够大的列表作为输入,则此功能最终将耗尽堆栈并崩溃。有几种方法可以解决此问题。
用于处理此类情况的经典Lisp-y方法之一是提供该函数的第二个实现,该函数将运行计数作为输入参数传递给“内部”函数:
(defn recursive-a-inner [cnt lst]
(cond
(seq lst) (cond
(= (first lst) "a") (recur (inc cnt) (rest lst))
:else (recur cnt (rest lst)))
:else cnt))
(defn recursive-a [& lst]
(recursive-a-inner 0 lst))
通过执行此操作,“内部”版本允许将递归推到尾部位置,以便可以使用Clojure的recur
关键字。它的实现不像原始的那么干净,但是它的优点是不会炸毁堆栈。
处理此问题的另一种方法是使用Clojure的loop
-ing,它允许在函数体内进行递归。结果与上面的“内部”功能基本相同:
(defn recursive-a [& lp]
(loop [cnt 0
lst lp]
(cond
(seq lst) (cond
(= (first lst) "a") (recur (inc cnt) (rest lst))
:else (recur cnt (rest lst)))
:else cnt)))
如果我们放弃了显式递归的要求,我们可以使其变得更简单:
(defn not-recursive-a [& lst]
(apply + (map #(if (= % "a") 1 0) lst)))
好运。
答案 2 :(得分:0)
本着学习的精神:
您是否可以使用&
。两者都很好。区别在于您随后将如何调用函数,并且您必须记住在重复执行时使用apply
。
此外,只需使用first
和rest
。它们都很安全,可以在nil和空列表上使用,分别返回nil和空列表:
(first []) ;; -> nil
(first nil) ;; -> nil
(rest []) ;; -> ()
(rest nil) ;; -> ()
这就是我如何重新设计您的想法:
;; With '&'
(defn count-a [& lst]
(if-let [a (first lst)]
(+ (if (= a "a") 1 0)
(apply count-a (rest lst))) ;; use 'apply' here
0))
;; call with variable args, *not* a list
(count-a "a" "b" "a" "c")
;; Without '&'
(defn count-a [lst]
(if-let [a (first lst)]
(+ (if (= a "a") 1 0)
(count-a (rest lst)))
0))
;; call with a single arg: a vector (could be a list or other )
(count-a ["a" "b" "a" "c"])
但是,这些方法并不安全,因为它们不使用尾递归,因此,如果您的列表很大,那么您将被淘汰!
因此,我们使用recur
。但是,如果您不想定义其他“帮助器”功能,则可以使用loop
作为“重复”目标:
;; With '&'
(defn count-a [& lst]
(loop [c 0 lst lst] ;; 'recur' will loop back to this point
(if-let [a (first lst)]
(recur (if (= a "a") (inc c) c) (rest lst))
c)))
(count-a "a" "b" "a" "c")
;; Without '&'
(defn count-a [lst]
(loop [c 0 lst lst]
(if-let [a (first lst)]
(recur (if (= a "a") (inc c) c) (rest lst))
c)))
(count-a ["a" "b" "a" "c"])
话虽如此,这也是我也会使用的:
;; With '&'
(defn count-a [& lst]
(count (filter #(= % "a") lst)))
(count-a "a" "b" "a" "c")
;; Without '&'
(defn count-a [lst]
(count (filter #(= % "a") lst)))
(count-a ["a" "b" "a" "c"])