我对 Clojure/Lisp 宏不是很熟悉。我想编写 apply-recur
宏,其含义与 (apply recur ...)
我想没有真正需要这样的宏,但我认为这是一个很好的练习。所以我要求你的解决方案。
最佳答案
嗯,真的没有必要,如果只是因为recur
不能采用可变参数(函数顶部的 recur
采用单个最终可序列参数,将所有参数分组并传递最后一个必需的参数)。当然,这不会影响练习的有效性。
但是,“适当的”apply-recur
存在一个问题大概应该处理由任意表达式返回的参数序列,而不仅仅是文字:
;; 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
不起作用),是一个 let
绑定(bind)本地而不是一个 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
(如果我们处理可变参数,最后的 nth
可能需要是 nthnext
,这会出现类似于下一段中描述的问题。另外,添加一个断言是个好主意检查 some-expression
返回的 seqable 的长度。)
我不知道有什么方法可以确定 recur
的正确数量在宏扩展时代码中的特定位置。这并不意味着一个不可用——这是编译器无论如何都需要知道的东西,所以也许有一种方法可以从其内部提取该信息。即便如此,任何执行此操作的方法几乎肯定都需要依赖将来可能会发生变化的实现细节。
因此结论是:即使完全有可能编写这样的宏(甚至可能不是这种情况),任何实现都可能非常脆弱。
最后,写一个 apply-recur
它只能处理文字(实际上arg seq的一般结构需要作为文字给出;参数本身——不一定,所以这可以工作:(apply-recur [foo bar baz])
=> (recur foo bar baz)
)会相当简单。我不会通过放弃解决方案来破坏练习,但作为提示,请考虑使用 ~@
.
关于macros - Clojure 中的应用循环宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3670010/