首先看一下之前自定义函数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也改了不下三次,前前后后用时三天左右,还是抽象和包装一层即可(不过此处不止一层)。


yangrd
1.3k 声望225 粉丝

代码改变世界,知行合一。