clojure - 实现直到作为宏
问题描述
这是我失败的尝试:
(defmacro until
[condition body setup increment]
`(let [c ~@condition]
(loop [i setup]
(when (not c)
(do
~@body
(recur ~@increment))))))
(def i 1)
(until (> i 5)
(println "Number " i)
0
(inc i))
我得到:CompilerException java.lang.RuntimeException:不能让限定名称:clojure-noob.core/c
我期待这个输出:
Number 1
Number 2
Number 3
Number 4
Number 5
怎么了?
解决方案
宏有几个问题:
- 您需要为宏内部的绑定生成符号。一种方便的方法是在名称后面加上
#
. 否则,宏中的绑定可能会掩盖代码中其他地方的绑定。 - 一些宏输入在未引用时被不必要地拼接了 ie
~@
而不是~
这是将编译/扩展的宏版本:
(defmacro until [condition body setup increment]
`(let [c# ~condition]
(loop [i# ~setup]
(when-not c#
~body
(recur ~increment)))))
但这将在您的示例中永远循环,因为condition
只评估一次并且i
' 的值无论如何都不会改变。我们可以解决这个问题:
(defmacro until [condition body increment]
`(loop []
(when-not ~condition
~body
~increment
(recur))))
i
如果我们想改变它的值,我们需要使其可变:
(def i (atom 1))
(until (> @i 5)
(println "Number " @i)
(swap! i inc))
;; Number 1
;; Number 2
;; Number 3
;; Number 4
;; Number 5
但是现在until
开始看起来很像 的补集while
,而且它的额外复杂性似乎没有什么好处。
(defmacro until [test & body]
`(loop []
(when-not ~test
~@body
(recur))))
这个版本until
是相同的,while
除了测试被反转,上面带有原子的示例代码仍然表现正确。我们可以until
通过while
直接使用进一步简化,最终会扩展为相同的代码:
(defmacro until [test & body]
`(while (not ~test) ~@body))
推荐阅读
- c++ - 什么是“结构黑客”和“类型/非类型隐藏”?
- swift - 通过邮递员和 URLSession 获取的数据在这里不同 http://173.249.20.137:9000/apiapp/coupon
- javascript - 导入组件时使用 JSX 错误时,get React' 必须在范围内
- c++ - stderr 和 stdout 的行为不同
- mysql - 将 MATCH 与 OR 一起使用会返回不满足任何条件的结果
- c++ - 如何将父小部件焦点重定向到子小部件?
- android - ImageView 没有形成卡片视图,在它工作正常之前,现在出了点问题,我无法弄清楚
- jquery - 为什么这个选择器在 jQuery 中不起作用?
- python - Python 类别方法
- java - 将文件中的数据添加到数组