macros - Clojure 中的应用循环宏

标签 macros clojure lisp

我对 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/

相关文章:

macros - 在 Common Lisp 中重命名 lambda

clojure - 如何映射要执行的 Java 方法的顺序

clojure - Clojure 中的循环加载依赖

multithreading - 使用 lisp 实现计算器程序

string - 将 sizeof() offsetof() 宏的结果嵌入到 const char 数组中?

c++ - 编译器之间的宏扩展顺序混淆

lisp - Clojure 变量和循环

lisp - SDRAW 在我的电脑上不工作

lisp - Emacs Lisp 映射函数名称列表并使用相同的 arg 调用它们

C++:关于运行时警告错误和消息的最佳实践是什么?