在可变参数函数中使用recur时出现意外行为

时间:2017-09-14 20:58:12

标签: clojure

当我需要为递归函数提供一个可选参数时,我正在为this挑战写一个答案。我最终得到的东西相当于:

(defn func [a & [b?]]
  (if b?
    b?
    (recur a a)))

我的意图是b?充当可选参数。如果没有提供,它将通过解构默认为nil

它没有运行,而是给了我一个错误:

(func 1)
UnsupportedOperationException nth not supported on this type: Long  clojure.lang.RT.nthFrom (RT.java:947)

经过一些调试后,我意识到由于某种原因,rest参数不是我期望的列表,而只是传递的数字!错误即将发生,因为它试图破坏数字。

我可以通过删除参数列表中的包装列表来修复它:

(defn func [a & b]
  ...

但这看起来不错。我知道其余参数应该是一个列表,但b实际上只是一个数字。如果我使用“未优化的”递归,它就像我期望的那样工作:

(defn func2 [a & [b?]]
  (if b?
    b?
    (func2 a a)))

(func2 1)
=> 1

有谁能解释这里发生了什么?

1 个答案:

答案 0 :(得分:10)

这似乎是known difference

; Note that recur can be surprising when using variadic functions.

(defn foo [& args]
  (let [[x & more] args]
    (prn x)
    (if more (recur more) nil)))

(defn bar [& args]
  (let [[x & more] args]
    (prn x)
    (if more (bar more) nil)))

; The key thing to note here is that foo and bar are identical, except
; that foo uses recur and bar uses "normal" recursion. And yet...

user=> (foo :a :b :c)
:a
:b
:c
nil

user=> (bar :a :b :c)
:a
(:b :c)
nil

; The difference arises because recur does not gather variadic/rest args
; into a seq.

这是描述差异的最后一条评论。