计算机程序的构造和解释(Lec5b:计算对象) ,整个影片都很值得看,但这篇要说的是从 46:35左右开始讲的,后面参杂了一点 lec6a。
最近开始学Clojure,所以内容中操作的部分会用clojure来呈现
任何的一个object的最小单位就是一个pair,而如何组成pair这个data structure呢,就是利用 cons , (cons x y)
就是一个pair。
对于任何的 x, y(car (cons x y)) = x(cdr (cons x y)) = y
但其实上述定义并没有阐述,到底cons是否有像人一样的身份(identity)?
未引入赋值
就上述的定义来说,其实是一种抽象,cons包含两个参数,如果两个cons包含的参数是一样的,那这两个cons就是一样的。
引入赋值后
上述定义就不完整了,因为如果我想修改两个包含一样参数cons的car,那两个cons都同时会被我修改吗?
如果两个cons代表的是数学,那其实没差,就算是两个一样的 3/4,就还是3/4,变成同把两个3/4变成1/4,反正就是1/4,并没有所谓 身份 的区别,
但物件导向的世界中,我们描述的是真实的世界,是有 身份 的,改变car可能就像是做变性手术一样der。
何谓身份:
(def a (cons 1 2))
这是说我在一个环境里面,创造了一个名为 a 的 pair,这个pair里面包含两个指针一个指向1,一个指向2。
接下来,我又定义了一个 b (def b (cons a a))
那 b 这个pair指向的两个a,是同一个a吗? 且现在呼叫 a 的方式有三种 a, (car b), (cdr b) ,哪个才是真正的呢?
而现在我用了 赋值去改 car b (set-car! (car b) 3)
,原本存在 a 的 1被改成3了。
现在如果再呼叫 (car a) 回传的会是 3,儘管一开始我们定义的 (car a) 是1。
非预期的共享,是大型系统的bug来源,透过给object一个身份 ,给它一个别名互相共享,是有蛮多方便,但也相对付出了代价。
演示一下用lambda凭空製造一个 cons ! by Alonzo Church
(defn cus-cons [x y] (fn [fc] (fc x y)))(defn cus-car [fc2] (fc2 (fn [a d] a)))(cus-car (cus-cons 1 2)) ;=> 1
演绎一下这个神奇的程序
1, 2被代换掉 cus-cons中的x, y,回传了一个function(fn [fc] fc 1 2)
传入 cus-car之后代换掉 fc2 : ((fn [fc] fc 1 2) (fn [a d] a))
, (fn [a d] a)
被当成参数代换掉 fc最后变成 ((fn [a b] a) 1 2)
,回传值就是1了,超级神奇吧!set 设计
稍微修改一下 church的算式
(defn cus-cons [x y] (fn [fc] (fc x y (fn [n] (let [x n])) (fn [n] (let [y n])))))(defn cus-car [fc2] (fc2 (fn [a d sa sd] a)))(defn cus-cbr [fc2] (fc2 (fn [a d sa sd] d)))(defn cus-set-car! [fc2 x2] (fc2 (fn [a d sa sd] (sa x2))))(defn cus-set-cdr! [fc2 y2] (fc2 (fn [a d sa sd] (sd y2))))
在cons的地方,新增两个lambda参数作为修改的认证用,这个set理论上是可行的,而有了一个set,就可以做千千万万个set了
Lambda这个方式,完全是用function来完成 cons car cdr,并没有存在任何一的地方,真的很有趣啊,之后一定要来看一下 这个传说中的 Alonzo church lambda calculus!!!
总结
回到赋值与state的讨论,当开始任意使用赋值,以下问题便开始产生
Change: 变数不再代表一个值,可能会被改变Time: 函式的回传值,可能会因为时序的不同而不一样Identity: pair不再只是用car cdr去呼叫,它可以有别称Share: 两个别称之间有可能会共享同一个pair有这么多问题产生,为何要这么做?
因为想构造 模块化 modularity的系统,对应真实世界的模型,而也许我们对于真实世界有些误解,也许时间只是一个幻觉,并不会改变什么,也许不用把时间切成一点一点分开看待,更宏观的来看成一个"时空"的路径。
如何解决:回到数位电路模拟器,可以以讯号处理的角度,而不是以单一时序的角度来看,如何做呢? 请继续看下去