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

问题描述

我不太熟悉Clojure / Lisp宏。我想写 apply-recur 宏,它将具有与(apply recur ...)相同的含义 p>

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

解决方案

好吧,真的没有必要,如果只是因为 recur 不能使用varargs( recur 到函数的顶部,只需要一个最后的seqable参数,所有参数传递最后一个必需的参数)。这不会影响练习的有效性。



但是,有一个问题,适当的 apply-recur 应该假设处理由任意表达式返回的参数seq,而不仅仅是文字:

 这应该工作... 
(apply-recur [1 2 3])

;; ...这应该有相同的效果...
(apply-recur(vector 1 2 3))

; ...如果这样,if(foo)返回[1 2 3]
(apply-recur(foo))

然而,任何表达式的值,例如(foo)根本不可用,一般来说,在宏展开时间。 (可能(向量1 2 3)可能被假定总是产生相同的值,但 foo 可能意味着不同的东西在不同的时间(一个原因 eval 将不工作),一个 let 另一个原因 eval 将无法工作等等)



因此写一个完全一般的 apply-recur ,我们需要能够确定常规重现形式期望的参数数量,并且具有 (apply-recur some-expression)展开为

 (let [seval#some-表达式] 
(recur(nth seval#0)
(nth seval#1)
...
(nth seval#n-1) n-1是最终参数的编号

(最后 / code>可能需要 nthnext 如果我们处理varargs,这提出了类似于下一段中描述的问题。一个好主意是添加一个断言来检查由 some-expression 返回的seqable的长度。)



我不知道任何方法来确定 recur 在宏扩展时代码中的特定位置的正确性。这并不意味着一个不可用 - 这是编译器需要知道的东西,所以也许有一种方法来从其内部提取信息。即使如此,任何这样做的方法几乎肯定需要依赖于将来可能改变的实现细节。



因此,结论是:即使它是所有可能写的这样的宏(这可能不是这种情况),很可能任何实现将是非常脆弱。






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


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.

解决方案

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.

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))

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.)

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

(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.)

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.


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中应用 - 重现宏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-12 11:12