我对Clojure / Lisp宏不是很熟悉。我想写一个与apply-recur
(apply recur ...)
宏
我想这个宏并不是真的需要,但我认为这是一个很好的练习。所以我要求你的解决方案。
答案 0 :(得分: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))
但是,在宏扩展时,一般来说,(foo)
这样的任意表达式的值根本不可用。 (可能假设(vector 1 2 3)
总是产生相同的值,但foo
可能在不同时间表示不同的事情(eval
不起作用的一个原因),是{{1} } -bound local而不是Var(另一个原因let
不起作用)等。)
因此,为了编写一个完全通用的eval
,我们需要能够确定常规apply-recur
形式所期望的参数数量,并使recur
扩展为类似
(apply-recur some-expression)
(如果我们正在处理varargs,那么最终的(let [seval# some-expression]
(recur (nth seval# 0)
(nth seval# 1)
...
(nth seval# n-1))) ; n-1 being the number of the final parameter
可能需要nth
,这会产生类似于下一段所述的问题。此外,这将是一个好主意添加断言以检查nthnext
返回的seqable的长度。)
我不知道在宏扩展时在代码中的特定位置确定some-expression
的正确arity的任何方法。这并不意味着一个不可用 - 这是编译器需要知道的东西,所以也许有一种方法可以从内部提取信息。即便如此,任何实现这一目标的方法几乎肯定需要依赖可能在未来发生变化的实施细节。
因此得出的结论是:即使完全可以编写这样的宏(甚至可能不是这种情况),任何实现都可能非常脆弱。
作为最后一句话,编写一个只能处理文字的recur
(实际上,arg seq的一般结构需要作为文字给出;论证本身 - 不一定,所以这可行:apply-recur
=> (apply-recur [foo bar baz])
)会非常简单。我不是通过放弃解决方案来破坏练习,但是,作为提示,请考虑使用(recur foo bar baz)
。
答案 1 :(得分:4)
apply
是一个将另一个函数作为参数的函数。 recur
是一种特殊形式,而不是函数,因此无法传递给apply
。