Positioning SICP 1: Building Abstractions with Procedures

Positioning SICP 1: Building Abstractions with Procedures #

作者:何岩,recreating.org出品,谢绝转载。
阅读[SICP],站在Lisp发明者的上帝视角来思考:“我为什么要这么设计Lisp?”

0.前言 #

本系列为Recreating Lisp的准备阶段。因为,硬核知识要基于SICP,所以,我需要一边读SICP一边构建“纯”定位体系。
文字就像是线,串起来的珍珠就是SICP中的Lisp代码。
所以,这本书的创造,换句话说,就是面向代码定位,而非面向概念。
第一本书是面向概念定位,因为概念容易描述清楚。
而本书很难面向概念定位,因为SICP是将如何编程的,很难之用文字描述清楚,代码的描述是最客观的。所以,这也决定了,这个书更适技术背景的读者。

WHAT - 本系列写的是什么?
定位,定位概念体系,定位Lisp的概念体系,定位为什么Lisp如此设计?以及,为什么这么使用Lisp来构建抽象?

WHY - 为什么要写定位?
1.因为,让自己站在创造者的视角,用自己的眼睛看世界,用自己的语言表达一遍SICP中讨论的各种概念,才能真懂。
2.另外,SICP的文字的并不是面向定位的,所以比较难看出结构来,而我的文字目的就是要表达定位,所以我的文字的价值能有倾向性。

HOW - 我如何写?
1.我要站在Lisp发明人的上帝视角不断的提问:“我为什么要设计这个概念?(Why I design this concept?)”
2.通过问题来引出SICP中的代码实现。
3.再即兴的加入感悟的文字,关联其他领域,例如:Bitcoin,哲学等生活体验。
4.最终呈现一个概念体系,体系的核心是Lisp。

1.Why - 我为什么要创造Lisp这个高级编程语言? #

为了构建抽象。
写到第二篇产生的感悟是:为了构建复杂,但是由于脑力限制,我们还要控制复杂。所以,编程就是既要构建复杂,又要控制复杂。解决的方法就是:抽象。

WHY - 为什么构建抽象?
为了模拟真实世界。

WHY - 为什么要模拟真实世界?
更高效的处理信息。

2.WHY - MIT的两位教授为什么要写SICP? #

为了让读者得到高级编程语言(以Lisp为代表)的本质:构建和解释。

Part1:构建(Structure),讲述了Lisp编程语言应该如何设计和使用,才能满足这样一个核心业务,即,构建抽象(Build Abstraction)。
构建抽象的核心挑战则是:控制复杂度(Control Complexity)

Part2:解释(Interpretation),讲述了在底层如何实现Lisp编程语言的存在,即,解释器(Interpreter)。
从不同的角度看,解释器分为软解释器和硬解释器。
软解释器就是Meta-Circular Evaluator,
硬解释器就是Lisp Machine(用硬件来实现Meta-Circular Evaluator)。
解释器的核心挑战就是,理解漩涡的内环:Eval/Apply

3.WHAT - 为了构建抽象世界,需要什么样的材料? #

Procedure就是抽象世界中的原子。而且有且只需要Procedure这唯一的类型。
这就像现实世界,从构建世界的材料视角来看,最小可组装单元,只有原子。分子由原子组成。量子/夸克无法自由组装,他们是原子的组成成分。
所以,Procedure就是原子。
(读了第二章之后,我觉得Procedure不是原子,Data才是原子。Procedure应该是夸克,是非物质的存在,是信息。而Data是被函数模拟出来的假象,是我们通过感受函数运转表现出来的色,所自动产生的假象体。所以说Data我们认为是物质实体,本质上则是空的,所以说,色即是空)

Lambda演算正是在表达这个思想:
1.Lambda表达式就是函数,任何其他元素都可以用Lambda表达式来构建。包括数字(丘奇数)和加法。
2.Lambda的律则,就像宇宙最初的物理规律一样,短短几条而已。

4.WHY - Lisp为什么选择Lambda演算作为内核? #

Lambda演算定义的那几条公理模拟了宇宙大道。
所以,Lambda演算足够接近真理。

6.Lisp与Lambda演算的关系 #

Lisp中的Procedure就是Lambda
而,Lisp的first-class就是procedure,可以说Lisp就是由procedure构建出来的大厦。

7.What - Lambda的本质是什么? #

λ演算是一个为了表达和计算函数的形式化系统,有着自己的化简规则和语法。整个系统是基于表达式的(也叫λ项)。

expression = variable || abstraction || application (也就是说这三种表达式都叫Lambda项)

1.组成-3条律则

8.图灵机和Lambda演算的关系是什么? #

不管是图灵机还是 lambda 演算都是为了解决可计算问题,也就是说图灵完备仅仅是 “可以计算出一个值”而不涉及其他的。函数式中将超过计算的东西叫做副作用,因为文件读写,打印,随机数,这些东西都不是纯的计算过程,而是涉及到外部世界的交互,依赖于机器,不在理论的范畴。你想写文件,那么是你的程序调用操作系统的 API,操作系统再执行 CPU 的指令,控制磁盘的芯片,磁头开始在盘面上写入原本位于内存或寄存器的信息。但是在图灵完备中,是不需要什么磁盘,磁头,内存的,这些都是理论之外的。也就是说一门图灵完备的语言,只要有无限的时间和空间,你绝对能用来解微分方程组,三体问题,甚至模拟整个宇宙,但这些语言可能不能在屏幕上打 Hello, world.

Chapter 1.1. The Elements of Programming #

1.WHY - 我为什么要创造Lisp? #

因为,要构建,构建逻辑系统,构建信息系统。
因为,要用一种语法,来表达逻辑的语义。

隐喻
使用咒语来召唤精灵,让精灵来帮忙做事。
定位:精灵就是process,咒语就是program。
Lisp只是一种咒语。不同的咒语,呈现不同的精灵形态。精灵还是同样精灵,但是行为模式根据咒语的范式而不同。

2.WHAT - 信息系统的实体是什么?答案是过程process #

1.abstract beings are process
2.process manipulate other abstract tings called data
3.And data is process

What - process和program的关系是什么?
1.program is rules that direct processes.
2.program is rules that construct data.

What - process的隐喻是什么?
A computational process is indeed much like a sorcerer’s idea of a spirit.
Process是巫师的精灵的思想。(不是精灵,而是精灵的思想)

WHY - 为什么不是精灵本身?而是思想?
因为,process是信息,精灵是物质,思想才是信息。
另外,精灵还是可以看得见摸得着的,思想是非物质的。
写在纸上的文字不是信息,运行于看不见的世界中的东西,才是思想。

WHAT - program的隐喻是什么?
The programs we use to conjure processes are like a sorcerer’s spells.
Program是巫师的咒语

Why - 为什么?
因为,咒语可以操纵精灵
真正干事情的是精灵
process是精灵的灵魂
硬件是精灵的肉体
灵魂知道肉体的行为,导致物理世界的改变—0/1电压的改变

WHAT - procedure和process的关系是什么?
The most significant of these features is the fact that Lisp descriptions of processes, called procedures, can themselves be represented and manipulated as Lisp data.
在Lisp中描述process的文字叫做procedure。
所以,咒语的载体就是procedure,咒语的具体内容就是具体的code。
一个procedure对应着一个未来跑在计算机里的process
另外,procedure有一个特性:Lisp的procedure自身也是Lisp data
即,代码即数据。第二章我们将会知道一种说法叫做闭合性,closure property。

Why - 为什么Lisp要将procedure设计成是Lisp Data?
The importance of this is that there are powerful program-design techniques that rely on the ability to blur the traditional distinction between “passive” data and “active” processes.
在Lisp中,procedures as data,这颠覆了这样一个传统—被动的data和主动的process。

这里要反问,为什么其他编程语言不将自身设计成data?
因为,其他语言妥协了,向自然语言妥协,想要讨好人类的语言习惯,所以放弃了形式的统一性。
而,Lisp没有妥协,保持了数学和自然语言的平衡,守住了底线,即语言即数据。语言即数学。咒语的内容可以是咒语本身,咒语可以谈论它自己。所以,Lisp天生就可以self-refer,就像人类可以天生自省。
Lisp可以思考他自己,就可以改变自己。这就是觉知力的本质。
也就是说,Lisp天生就具有觉知力。
如果将Lisp比喻成人类,那么其他编程语言就是动物。

3.Why I design the framework 我为什要设计出构建语言的框架思想? #

— Chapter 1.1 The Elements of Programming
因为,这是一个足够抽象的模式
如果你想构建任何系统,只需要将问题分解成多个层级,在每个层级重复的做这3件事:
1.Primitive
2.Combination
3.Abstraction
其中,Primitive就是之前层次的Abstraction,本层的Abstraction又是为下一个层提供的Primitive。如此组成了一个循环。分与合一体两面,同一个事物,即是分又是和。
…Combination—>Primitive(last Layer’s Abstraction)—>Combination—>Abstraction(as Primitive for next Layer)—>Combination…

What - 什么是语言的元素?
The Elements of Programming
1.Primitive expressions

效果如下:
486
486

(+ 137 349)
486

(* 5 9)
495

*Why - I need expressions? (— Chapter 1.1.1 Expressions) *
因为,如果要构建一门PL,第一步就是设计最小组成粒度的材料。
就像,乐高基础块之于乐高作品。
就像,砖头之于盖房子。

2.Means of combination
by which compound elements are built from simpler ones
apply应用:表达式apply表达式
表达式的构建:表达式嵌套表达式

Why - I need combination ? (— Chapter 1.1.1 Expressions)
因为,有了基础块,就要考虑如何拼装基础块。
就像,乐高块的凸起按钮的设计。
自由组合效果如下:
(+ (* 3 5) (- 10 6))
19

3.Means of abstraction
by which compound elements can be named and manipulated as units.
define
refer
效果如下:
(define pi 3.14159)

(define radius 10)

(* pi (* radius radius))
314.159
WHY - I need naming? (— Chapter 1.1.2 Naming and the Environment)
因为,可以给抽象一个定位点/抓手。
为了,维持一个状态:name/Object pair 引出,Environment
Environment的本质:一块内存,存储key/value的store。

HOW - Environment的启发?
redis这样的key/value内存数据库。
可以构建分布式的interpreter。
网络版的lisp语言。工程化的网络化的编程语言。XLISP

HOW - 指导意义?
1.谁封装的更彻底/更友好,他就越容易赚到钱—找到商业模式。
一定要让人们,方便/愉悦的使用你提供的封装好的服务。
2.例如,apple的产品,将数据封装到罐子里,应用都不可以收工copy安装文件。反例windows的系统。
3.不让用户看到更多的选择,反而是一种用户友好行为。

WHY - 为什么让Procedures可以自由组合?
因为,简单原则,复杂行为
所以,要让规则尽量简单,Procedure就是一切的基础,是first-class
procedure组合的效果如下:
当Procedure可以被抽象,即被define成变量,procedure就有了互相自由引用的能力refer ability,虽然过多的互相refer大大的增加的复杂性,破坏了black-box abstraction的思想原则。

我们来看看procedure compound的效果:
;定义一个变量square的procdeure,参数为x,procedure的body是求平方
(define (square x) (* x x))

(define (sum-of-squares x y)
(+ (square x) (square y)))

(sum-of-squares 3 4)
25

(define (f a)
(sum-of-squares (+ a 1) (* a 2)))

4.Why - I design Evaluating Combinations?我为什么要设计求值这个概念? #

— Chapter 1.1.3 Evaluating Combinations
因为,要计算,要动起来,要运行。
因为,计算的本质就是组装,from lambda calculate
因为,组装的本质就是求值,from lisp
求值的主角是:combination

To evaluate a combination, do the following:
1.Evaluate the subexpressions of the combination.
2.Apply the procedure that is the value of the leftmost subexpression(the operator ) to the arguments that are the values of the other subexpressions(the operands)

Apply的本质是Bate规约,即,组装
Evaluate的本质是递归的调用Apply,直到遇到primitive element

所以,Apply和Evaluate形成了一个递归的内环。
Eval/Apply就是Lisp漩涡模型的核心内环。
Eval/Apply隐喻着宇宙的中心,旋转。

How - 对我的启发是什么?
找到我的漩涡的核心,每天要做的事就是重复的旋转它,就是在对我的事求值。

5.Why - I design Substitution Model? 我为什么设计Substitution Model? #

** — Chapter 1.1.5 The Substitution Model for Procedure Application**
因为,求值的模式有两种。
第一种:Applicative order,即,先求值参数operands,再带入到operator
第二种:Normal order,即,后求值参数operands,什么时候要用到再求值

Applicative order的效果如下:
(f 5)
(sum-of-squares (+ 5 1) (* 5 2))
(+ (square 6) (square 10))
(+ (* 6 6) (* 10 10))
(+ 36 100)
136

Normal order的效果如下:
(f 5)
(sum-of-squares (+ 5 1) (* 5 2))
(+ (square (+ 5 1)) (square (* 5 2)))
(+ (* (+ 5 1) (+ 5 1)) (* (* 5 2) (* 5 2)))
(+ (* 6 6) (* 10 10)
(+ 36 100)
136

第二种像太极拳,先不发力,先蓄势,最后一下子再发力。这样可以省了很多过程中的发力,过程就是顺着走。但是难学难懂,很难工程化教学。(stream view)
第一种很像外家拳,按照招式走,一招是一招。(object view)

6.Why - I design Conditional Expressions and Predicates?我为什么设计了Conditional Expressions #

** — Chapter 1.1.6 Conditional Expressions**
因为,如果没有过程中的test求值能力,构建的过程很受限制。
所以,为了让编程更有威力,即,更好的控制复杂度,所以,引入了condition Expressions.
本质,就是,过程中的特殊expression的求值。
所以,condition expression is special form procedure, not as the normal procedure.
所以,interpreter应该是对condition expression做了特殊处理,走了不同于procedure的逻辑分支。

例如:excercise 1.6
new-if与 if的不同之处,在于参数的求值。
=> (if #t (display “good”) (display “bad”)
good

=> (new-if #t (display “good”) (display “bad”)
badgood

7.Why - I import the example : Square Roots by Newton’s Method? 我为什么要引入牛顿解平方根的例子? #

** — Chapter 1.1.7 Example:Square Roots by Newton’s Method**
因为,要定位,mathematical与procedure的不同,即,mathematical is what, procedure is how-to
因为,要定位,function与procedure的不同。function is belongs to mathematical.
因为,要展示,抽象的描述how-to
因为,要展示,递归。
(define (sqrt-iter guess x)
(if (good-enough? Guess x)
guess
(sqrt-iter (improve guess x)
x)))
哪怕只有一句逻辑,也可以封装成抽象abstraction,如下:
(define (improve guess x)
(average guess (/ x guess)))

(define (average x y)
(/ (+ x y) 2))

(define (good-enough? Guess x)
(< (abs (- (square guess) x)) 0.001))

(define (square x)
(* x x)

8.Why - I import the thought of Black-Box Abstraction?我为什么要提出黑盒抽象这个思想? #

— Chapter 1.1.8 Procedures as Black-Box Abstractions
因为,要降低系统复杂性。
如果,所有子问题都开放,系统的关系链接就会过多,互相依赖过多,复杂性就暴涨了。
为了,让复杂性内聚,也就是让关系变少。所以才要构建Black-Box
具体如何让Lisp实现呢?
1.作用域的private
2.内部定义过程
3.block structure

What?本质上是什么?
本质上是procedures的组合。
之前有了expressions的组合。
在lisp中任意元素都可以自由组合。所以说,lisp像水。
后面会遇到Higher-Order Procedure, Closure Property都是基于Black-Box Abstraction这个思想。

How?启发?
1.在metanet上构建一个分布式PL。
2.将任何程序都看成一棵树
3.所以,要将程序写成形状均匀的树。大多数人的程序都不平衡。

Chapter 1.2 Procedures and Processes They Generate #

1.Why I need to test the running of procedures and processes? #

因为,我们需要练习“看见”代码。即,将代码解释成图像。

How to becoming an expert programmer?
The ability to visualize the consequences of the actions under consideration is crucial to becoming an expert programmer, just as it is in any synthetic, creative activity.
Why?
因为,这样会让我的神经元接近lisp的模式。将我的大脑驯化成Lisp Interpreter。
我的大脑,要成为这样一个Lisp Interpreter,就像黑客帝国一样,见到代码就能自动翻译成图像。

2.Why I talk about the two different Loop way: Linear Recursion and Iteration #

— Chapter 1.2.1 Linear Recursion and Iteration
因为,我们要尝试不同的递归模式
在这两种循环中,我们可以尝到循环的本质。
状态的本质。
空间复杂度的区别。

What - 两者的本质不同是什么呢?
1.linear recursive : 面向过程(没有时间)(无状态)
2.linear iterative:面向对象(时间因素)(有状态)(将状态放在变量中,即,enviorment)

对于同一个需求,求factorial : n!=n·(n-1)·(n-2)….3·2·1
linear recursive的实现如下:注意求值时候的shape
(define (factorial n)
(if (= n 1)
1
(* n (factorial (- n 1)))))

(factorial 6)
(* 6 (factorial 5))
(* 6 (* 5 (factorial 4)))
(* 6 (* 5 (4 (factorial 3))))
(
6 (* 5 (4 (*3 (factorial 2))))
(
6 (* 5 (4 (*3 (*2 (factorial 1)))))
(
6 (* 5 (4 (*3 (*2 1))))
(
6 (* 5 (4 (*3 2)))
(
6 (* 5 (4 6)))
(
6 (* 5 24))
(* 6 120)
720
linear interative的实现如下:注意求值时候的shape
(define (factorial n)
(fact-iter 1 1 n))

(define (fact-iter product counter max-count)
(if (> counter max-count)
product
(fact-iter (* counter product)
(+ counter 1)
max-count)))

(factorial 6)
(fact-iter 1 1 6)
(fact-iter 1 2 6)
(fact-iter 2 3 6)
(fact-iter 6 4 6)
(fact-iter 24 5 6)
(fact-iter 120 6 6)
(fact-iter 720 6 6)
720

Chapter 1.3 Formulating Abstractions with Higher-Order Procedures #

1.Why I design the concept Higher-Order Procedures? #

This would place us at a serious disadvantage, forcing us to work always at the level of the particular operations that happen to be primitives in the language(multiplication, in this case) rather than in terms of higher-level operations.

Our programs would be able to compute cubes, but our language would lack the ability to express the concept of cubing.

Yet even in numerical processing we will be severely limited in our ability to create abstractions if we are restricted to procedures whose parameters must be numbers. Often the same programming pattern will be used with a number of different procedures.

To express such patterns as concepts, we will need to construct procedures as values.

Procedures that manipulate procedures are called higher-order procedures.
因为,这是尊从Black-Box Abstraction这个思想的演进。让Lisp更加自由,自由组后,Procedure可以作为First-class自由流转穿梭。
因为,要构建模型。即,某些算法部分不变,某些算法可变。不变的就是模型,将可变的部分开放出去,这是一种更高级的抽象。缠在一起的庖丁解牛级别的抽象。曲线的抽象。
启发,比特币的脚本,就是这种对外开放的procedures。潜入bitcoin内部的渠道。

抽象—链接—>抽象
这里的链接是内部深层的链接。
我之前的编程思维还只停留在refer这种简单链接维度。这就好比同样是解牛,我只能大块的砍,庖丁能深入的解牛。

What - 抽象视角是什么?
Procedure is Transaction
Transaction‘s input: Procedures as Arguments (Chapter 1.3.1)
Transaction’s output: Procedures as Returned Values (Chapter 1.3.4)
Procedure is the first-class element as Tx in bitcoin.
Procedures从此就像LEGO基础块有了上下链接的凸凹按钮。
Procedure/TX的链接
Procedure/TX的流通

2.Why I design Lambda? #

— Chapter 1.3.2 Constructing Procedures Using Lambda
因为,能够直接定义Procedure
(Rather than define pi-next and pi-term, it would be more convenient to have a way to directly specify “the procedure that returns its input incremented by 4” and “the procedure that returns the reciprocal of its input times its input plus 2.” We can do this by introducing the special form lambda,)
Why?为什么需要直接定义Procedure?
因为,更加接近procedure的本体。匿名的procedure就是procedure本身。
因为,省去了define,就不需要refer,代码会更加紧凑,直白。可以在代码中,直接操作procedre,不用像以前那样,需要先定义,再引用,最终才能操作。
因为,define需要维护name-value对,在environment里。而lambda不需要,所以更简单,省事,按理说,效率也会提升。

Why I design let?
因为,let降低了思考的复杂度。所以,let是为人脑服务的,而不是为了程序服务。所以,let是糖,是给人脑吃的。程序根本没有感觉。
本质,let的本质就是一种语法糖,就是通过应用lambda的方式连接lambda的参数另一种表达形式。实质性的信息没有变化,只是表面功夫。

3.Why I design Higher-Order Procedures with these 2 ways: Procedures as Arguments & Procedures as Returned Values #

1.Procedures as Arguments: Ability of LEGO Block Input
本质,Procedures就像LEGO块,有了链接上游的凸纽。

2.Procedures as Returned Values: Ability of LEGO Block output
本质,Procedures就像LEGO块,有了链接下游的凹钮

这样一来,Procedure就可以自由穿梭于任何其他的Procedure。无限自由的组合可能性开启。

 
1
Kudos
 
1
Kudos

Now read this

第四章.挥舞函数(4.Wielding functions)[完成]

翻译 Secrets of the JavaScript Ninja (JavaScript忍者禁术) 第四章.挥舞函数(4.Wielding functions) 本章重点: # 1.为什么匿名函数如此重要 # 2.函数中的递归 # 3.函数可以被引用后再调用 # 4.如何为函数缓存索引 # 5.利用函数的能力来实现记忆 # 6.利用函数上下文 # 7.处理参数长度 # 8.判断一个对象是否为函数 # 在上一章我们了解到函数作为自然类型的对象(first-order... Continue →