在Clojure中应用-recur宏

Joe*_*mel 14 lisp macros clojure

我对Clojure/Lisp宏不是很熟悉.我想写apply-recur一个与其含义相同的宏(apply recur ...)

我想这个宏并不是真的需要,但我认为这是一个很好的练习.所以我要求你的解决方案.

Mic*_*zyk 14

好吧,确实没有必要,如果只是因为recur不能采取varargs(recur函数的顶部采用单个最终的seqable参数分组,所有参数都传递最后一个必需的参数).当然,这不会影响练习的有效性.

但是,存在一个问题,即"正确" apply-recur应该可能处理由任意表达式返回的参数seqs,而不仅仅是文字:

;; this should work...
(apply-recur [1 2 3])

;; ...and this should have the same effect...
(apply-recur (vector 1 2 3))

;; ...as should this, if (foo) returns [1 2 3]
(apply-recur (foo))
Run Code Online (Sandbox Code Playgroud)

但是,(foo)一般来说,在宏扩展时,任意表达式的值根本不可用.(也许(vector 1 2 3)可以假设它总是产生相同的值,但foo可能在不同的时间意味着不同的东西(一个原因eval不起作用),是一个let本地而不是一个Var(另一个原因eval是行不通的)等等)

因此,为了编写一个完全通用的apply-recur,我们需要能够确定常规recur表单所期望的参数数量,并将其(apply-recur some-expression)扩展为类似的参数

(let [seval# some-expression]
  (recur (nth seval# 0)
         (nth seval# 1)
         ...
         (nth seval# n-1))) ; n-1 being the number of the final parameter
Run Code Online (Sandbox Code Playgroud)

(如果我们正在处理varargs,最后nth可能需nthnext要这样做,这会产生类似于下一段所描述的问题.另外,添加一个断言以检查返回的seqable的长度是个好主意.some-expression.)

我不知道有任何方法可以recur在宏扩展时确定代码中特定位置的正确arity .这并不意味着一个不可用 - 这是编译器需要知道的东西,所以也许有一种方法可以从内部提取信息.即便如此,任何实现这一目标的方法几乎肯定需要依赖可能在未来发生变化的实施细节.

因此得出的结论是:即使完全可以编写这样的宏(甚至可能不是这种情况),任何实现都可能非常脆弱.


作为最后的评论,写一个apply-recur只能处理文字的东西(实际上,arg seq的一般结构需要作为文字给出;参数本身 - 不一定,所以这可以工作:(apply-recur [foo bar baz])=> (recur foo bar baz))会很简单.我不是通过放弃解决方案来破坏练习,但是,作为提示,考虑使用~@.


Stu*_*rra 5

apply是一个将另一个函数作为参数的函数。recur是一种特殊形式,而不是函数,因此不能传递给apply.