Clojure 中的 Apply-recur 宏 [英] Apply-recur macro in Clojure

查看:16
本文介绍了Clojure 中的 Apply-recur 宏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 Clojure/Lisp 宏不是很熟悉.我想编写 apply-recur 宏,它与 (apply recur ...)

I'm not very familiar with Clojure/Lisp macros. I would like to write apply-recur macro which would have same meaning as (apply recur ...)

我想没有真正需要这样的宏,但我认为这是一个很好的练习.所以我在寻求你的解决方案.

I guess there is no real need for such macro but I think it's a good exercise. So I'm asking for your solution.

推荐答案

嗯,真的没有必要,如果只是因为 recur 不能接受可变参数(一个 recur 到函数的顶部需要一个最终的 seqable 参数,将所有参数分组并传递最后一个必需参数).当然,这并不影响练习的有效性.

Well, there really is no need for that, if only because recur cannot take varargs (a recur to the top of the function takes a single final seqable argument grouping all arguments pass the last required argument). This doesn't affect the validity of the exercise, of course.

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

However, there is a problem in that a "proper" apply-recur should presumably handle argument seqs returned by arbitrary expressions and not only literals:

;; 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 可能在不同时间意味着不同的东西(一个原因 evalcode> 不起作用),成为 let 绑定的本地而不是 Var(eval 不起作用的另一个原因)等)

However, the value of an arbitrary expression such as (foo) is simply not available, in general, at macro expansion time. (Perhaps (vector 1 2 3) might be assumed to always yield the same value, but foo might mean different things at different times (one reason eval wouldn't work), be a let-bound local rather than a Var (another reason eval wouldn't work) etc.)

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

Thus to write a fully general apply-recur, we would need to be able to determine how many arguments a regular recur form would expect and have (apply-recur some-expression) expand to something like

(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 的长度.)

(The final nth might need to be nthnext if we're dealing with varargs, which presents a problem similar to what is described in the next paragraph. Also, it would be a good idea to add an assertion to check the length of the seqable returned by some-expression.)

我不知道有什么方法可以在宏扩展时确定代码中特定位置 recur 的适当数量.这并不意味着一个不可用——无论如何这是编译器需要知道的,所以也许有一种方法可以从其内部提取该信息.即便如此,任何实现这一点的方法几乎肯定都需要依赖于未来可能会发生变化的实现细节.

I am not aware of any method to determine the proper arity of a recur at a particular spot in the code at macro-expansion time. That does not mean one isn't available -- that's something the compiler needs to know anyway, so perhaps there is a way to extract that information from its internals. Even so, any method for doing that would almost certainly need to rely on implementation details which might change in the future.

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

Thus the conclusion is this: even if it is at all possible to write such a macro (which might not even be the case), it is likely that any implementation would be very fragile.

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

As a final remark, writing an apply-recur which would only be capable of dealing with literals (actually the general structure of the arg seq would need to be given as a literal; the arguments themselves -- not necessarily, so this could work: (apply-recur [foo bar baz]) => (recur foo bar baz)) would be fairly simple. I'm not spoiling the exercise by giving away the solution, but, as a hint, consider using ~@.

这篇关于Clojure 中的 Apply-recur 宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆