Tuo*_*nen 6 lisp macros functional-programming function clojure
我正在学习Clojure宏,并想知道为什么我们不能只使用函数进行元编程.
据我所知,宏和函数之间的区别在于宏的参数没有被计算,而是作为数据结构和符号传递,而返回值被计算(在调用宏的地方).Macro作为读者和评估者之间的代理,在评估发生之前以任意方式转换表单.在内部,他们可以使用所有语言功能,包括函数,特殊形式,文字,递归,其他宏等.
功能正好相反.在调用之前计算参数,返回值之后不返回值.但宏和函数的镜像特性让我想知道,我们不能通过引用它们的参数(表单),转换表单,在函数内部进行评估,最终返回它的值来使用函数作为宏.这在逻辑上不会产生相同的结果吗?当然这会很不方便,但从理论上讲,每个可能的宏都有相同的功能吗?
这是简单的中缀宏
(defmacro infix
"translate infix notation to clojure form"
[form]
(list (second form) (first form) (last form)))
(infix (6 + 6)) ;-> 12
Run Code Online (Sandbox Code Playgroud)
这是使用函数的相同逻辑
(defn infix-fn
"infix using a function"
[form]
((eval (second form)) (eval (first form)) (eval (last form))))
(infix-fn '(6 + 6)) ;-> 12
Run Code Online (Sandbox Code Playgroud)
现在,这种看法是否适用于所有情况,还是存在一些宏观无法超越的极端情况?最后,宏只是函数调用的语法糖吗?
Thu*_*ail 11
如果我在回答之前阅读这个问题会有所帮助.
除了文字之外,你的中缀函数不起作用:
(let [m 3, n 22] (infix-fn '(m + n)))
CompilerException java.lang.RuntimeException:
Unable to resolve symbol: m in this context ...
Run Code Online (Sandbox Code Playgroud)
这是@jkinski所指出的结果:当时eval行为m已经消失.
宏可以做什么功能不可以?
是.但是如果你能用一个函数来做,你通常应该这样做.
宏是有益的
功能无法做到.
延期评估
考虑(来自Halloway&Bedra的编程Clojure)
(defmacro unless [test then]
(list 'if (list 'not test) then)))
Run Code Online (Sandbox Code Playgroud)
... ...的部分克隆if-not.我们用它来定义
(defn safe-div [num denom]
(unless (zero? denom) (/ num denom)))
Run Code Online (Sandbox Code Playgroud)
......这会阻止除零,返回nil:
(safe-div 10 0)
=> nil
Run Code Online (Sandbox Code Playgroud)
如果我们试图将其定义为函数:
(defn unless [test then]
(if (not test) then))
Run Code Online (Sandbox Code Playgroud)
... 然后
(safe-div 10 0)
ArithmeticException Divide by zero ...
Run Code Online (Sandbox Code Playgroud)
在忽略它之前,潜在结果被评估为then参数.unlessunless
捕获表单和重新组织语法
假设Clojure没有case形式.这是一个粗略的替代品:
(defmacro my-case [expr & stuff]
(let [thunk (fn [form] `(fn [] ~form))
pairs (partition 2 stuff)
default (if (-> stuff count odd?)
(-> stuff last thunk)
'(constantly nil))
[ks vs] (apply map list pairs)
the-map (zipmap ks (map thunk vs))]
(list (list the-map expr default))))
Run Code Online (Sandbox Code Playgroud)
这个
ks)和相应的表达式(vs),fn形式,细节并不重要.关键是它可以做到.
当Guido van Rossum建议向Python添加一个案例陈述时,委员会拒绝了他.因此Python没有案例陈述.如果里奇不想要case陈述,但我做了,我可以有一个.
只是为了好玩,让我们使用宏来设计if表单的可复制克隆.这无疑是功能编程界的陈词滥调,但让我感到意外.我曾经认为if是懒惰评价的不可简约原语.
一个简单的方法是捎带my-case宏:
(defmacro if-like
([test then] `(if-like ~test ~then nil))
([test then else]
`(my-case ~test
false ~else
nil ~else
~then)))
Run Code Online (Sandbox Code Playgroud)
这是冗长和缓慢的,它使用堆叠和丢失recur,它被埋在封闭物中.但是......
(defn fact [n]
(if-like (pos? n)
(* (fact (dec n)) n)
1))
(map fact (range 10))
=> (1 1 2 6 24 120 720 5040 40320 362880)
Run Code Online (Sandbox Code Playgroud)
......它或多或少都有效.
亲爱的读者,请指出我的代码中的任何错误.