强烈谴责 b 大给别人挖坑的行为 hello interpreter: http://ruby-china.org/topics/11301
一般编程语言的函数调用都是 strict order 的,就是所有参数传给一个函数之前,会将所有参数求值,再跑进函数体里。但有些表达式/语句结构的组成部分却是延迟的,例如 or 表达式的短路效果:
def f
puts 'f'
true
end
def g
puts 'g'
false
end
f or g # 只有 f 调用了, g 没调用
f and g # g 调用了
所以在 ruby 里,and
和 or
是不能重新定义的。
如果把 if ... else 当成一个表达式
(if-else cond true-clause false-clause)
那表达式的 true-clause 和 false-clause 都是延迟执行的。到底执行哪个要进入到 if-else 函数体内才能决定。
为了定义条件语句和支持短路的布尔运算符,SICP 的 lazy 其实是定义了两个 primitive:
(delay-it exp)
对 exp 延迟求值(force-it exp)
无视状况对 exp 立刻求值全盘 lazy 是什么样子?就是所有的 exp 都变成了 (delay-it exp)
. 那么 (force-it exp)
也变成了 (delay-it (force-it (delay-it exp)))
执行不下去了。所以至少 force-it
不能做 lazy 变换,某些默认函数也要添加 force-it
去让程序能跑下去。
默认 lazy 的 haskell, 可以理解为 IO monad 自动给表达式加上了 force-it
, 并且通过类型限制,禁止在 force-it
的外面加上 delay-it
.
back to erlang
delay 直接和 quote 差不多 force 就要调用一个不同版本的 apply, 这个 apply 的 prim 里要忽略掉 delay
首先把 b 大的代码改造下...
- module(sexp).
- compile({no_auto_import, [apply/2]}).
- compile(export_all).
% make:all([load]).
eval(Env, []) ->
{Env, []};
eval(Env, [H|T]) ->
{Env1, RH} = apply(Env, H),
{Env2, RT} = eval(Env1, T),
{Env2, [RH|RT]}.
apply(Env, Atom) when is_atom(Atom) ->
{Atom, Value} = proplists:lookup(Atom, Env),
{Env, Value};
apply(Env, [Op|Args]) ->
{Env1, Func} = apply(Env, Op),
Func(Env1, Args).
quote(Env, [X]) ->
{Env, X}.
new_env() ->
[
{quote, fun quote/2},
].
SICP 里只有 quote, atom 就不搞了
其中通过 fun quote/2
获取了 lambda object, 就不需要加个 prim
去手工 dispatch 了。
make:all([load]).
E = sexp:new_env().
{_, a} = sexp:apply(E, [quote, a]).
{_, quote} = sexp:apply(E, [quote, quote]).
{_, [a, b, c]} = sexp:apply(E, [quote, [a, b, c]]).
force 的实现,就是先把环境改造下,插个 delay = nodelay 进去,然后对 AST 求值,然后再恢复环境
force(Env, [X]) ->
Env1 = [{delay, fun nodelay/2}|Env],
{Env2, Value} = apply(Env1, X),
{[{delay, fun delay/2}|Env2], Value}.
delay 和 nodelay
delay(Env, [X]) ->
{Env, X}.
nodelay(Env, [X]) ->
{Env1, Value} = apply(Env, X),
{Env, Value}.
现在终于可以定义 ifelse 了... 回忆 lambda calculus, true 和 false 可以定义为下面这俩货:
true = λ x _ . x
false = λ _ y . y
翻译成 erlang 就是
truef(Env, [X, _]) ->
apply(Env, X).
falsef(Env, [_, Y]) ->
apply(Env, Y).
ifelse(Env, [Pred, True, False]) ->
{Env1, Value} = apply(Env, Pred),
Value(True, False).
(写完回头一看,诶!为什么没用 delay 和 force 实现!) ...
最后补充下 new_env (apply 和 eval 也能放进去的呀干嘛不放)
new_env() ->
[
{true, fun truef/2},
{false, fun falsef/2},
{eval, fun eval/2},
{apply, fun apply/2},
{quote, fun quote/2},
{delay, fun delay/2},
{force, fun force/2},
{ifelse, fun ifelse/2},
].
代码能否跑还没测,喝个粥先...