首先看一下之前自定义函数if的效果
(
(define if (lambda (p then_v else_v)
((or (and p car) cdr) (cons then_v else_v))))
(if (quote (> 1 2)) (3) (cons 2 4))
)
看起来还可以,但是因为涉及入参绑定值的时候会对传入的s表达式进行求值,如果涉及递归的时候就会出问题,因此我们需要增加一个quote, 但这样也导致了新的问题 如果我们想延迟对传入s表达式求值,就需要每一个表达式都进行修饰,无疑增加了许多心智负担,那如何将负担降到最低呢?
我们的期望我们的表达式变成这样:
(if (> 1 2) 3 (cons 2 4))
首先我们想到的是将上面表达式展开后期望的效果是什么样子的?
((or (and (> 1 2) car) cdr) (cons 3 (cons 2 4)))
这个时候我们可以用 apply 函数来实现
在开始前我们先对if函数进行改造
(define if (lambda (p then_v else_v)
(list (list (quote or) (list (quote and) p (quote car)) (quote cdr)) (list (quote cons) then_v else_v)))))
这个时候我们可以用apply 函数进行调用 ,apply 函数的作用是调用函数时 会当最后一个参数是列表或引用时会对其进行展平 如下
(apply if (` ((> 1 2) 3 (cons 2 4))))
=> ((or (and (> 1 2) car) cdr) (cons 3 (cons 2 4)))
等价于
( if (`(> 1 2)) (` 3) (`(cons 2 4)))
=> ((or (and (> 1 2) car) cdr) (cons 3 (cons 2 4)))
他们都会输出一个复合我们期望的列表
这个时候我们只需要再次对其进行调用即可,如
(apply(apply if (` ((> 1 2) 3 (cons 2 4)))))
细心的你已经发现了在定义if函数时我们用来了大量的list 函数与 quote 函数,看起来很麻烦,这里使用一种简化的写法
(define if (lambda (p then_v else_v)
(`(
(or (and , p car) cdr)
(cons , then , else) ))))
引用类型中如何有 , 符号则对其下一个元素进行eval,否则忽略。
其中的核心便是两个apply 首先第二个apply执行会得到表达式 第一个会拿到表达式进行调用 从而拿到最终的值。
(apply(apply if (` ((> 1 2) 3 (cons 2 4)))))
核心便是把这一段话模式如下
(apply(apply exp (` exp)))
然后我们进行封装代码如下:
private static Object defineMacro(ApplyArgs applyArgs) {
Cons cdr = applyArgs.getExp();
validateTrue(applyArgs.getEnv().noContains(cdr.carSymbols()), "Do not repeat the definition " + cdr.carSymbols());
Function<ApplyArgs, Object> applyFun = (applyArgs1) -> {
Cons cons = markList(Symbols.of("apply"), cdr.cdr().car(), markQuote(applyArgs1.getExp().list().toArray()));
return applyArgs1.eval(markList(Symbols.of("apply"),applyArgs1.eval(cons)));
};
applyArgs.getEnv().setEnv(cdr.carSymbols(), applyFun);
return null;
}
然后注册
reg("define-macro", FunManager::defineMacro);
这个时候我们的if函数就可以通过宏来实现了,宏可以理解成生生成列表的工具,然后其中用到了 apply , quote 模板渲染等技术。
(define-macro if (lambda (p then . else) (
`(
(or (and , p car) cdr)
(cons , then , else)))))
这个时候我们的if 的 p then_v else_v 就可以不再用 quote 进行修饰了。
(if (> 1 2) 3 (cons 2 4))
现在,我们回到了最初的起点。
总结
其中宏实现了两版,apply 函数改了4-5次,quote也改了不下三次,前前后后用时三天左右,还是抽象和包装一层即可(不过此处不止一层)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。