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

Car*_*ate 7 clojure

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

(defn func [a & [b?]]
  (if b?
    b?
    (recur a a)))
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

(defn func [a & b]
  ...
Run Code Online (Sandbox Code Playgroud)

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

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

(func2 1)
=> 1
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*ter 10

这似乎是一个已知的差异

; 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.
Run Code Online (Sandbox Code Playgroud)

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