atom
是什么? - 字符串 - 符号 - 数字 - 字符
[6]:
(atom? 'atom)
[6]:
#t
[7]:
(atom? 123)
[7]:
#t
[8]:
(atom? (quote a))
[8]:
#t
[9]:
(atom? "abc")
[9]:
#t
list
是什么? - 括号包围? - 一组括号包围的atom
? - 一组括号包围的S-expression
car
, 列表第一个元素 cdr
, 列表除第一个外的元素列表
(cons a. b)
, a
为任一S-expression
, b
为任一列表.
[10]:
(list? '(atom))
[10]:
#t
[11]:
(list? '(atom turkey or))
[11]:
#t
[12]:
(list? '())
[12]:
#t
[13]:
(atom? '()) ;; '() 表示空, 和空列表的意义重叠了
[13]:
#t
[14]:
(list? '(() () () ()))
[14]:
#t
[15]:
(car '(((hotdogs)) (and) (pickle) relish))
[15]:
((hotdogs))
[16]:
(car '()) ;; car处理非空列表
Traceback (most recent call last):
File "In [16]", line 1, col 1, in 'car'
File "In [16]", line 1, col 1
RunTimeError: car called on non-pair ()
[17]:
(cdr '()) ;; cdr处理非空列表
Traceback (most recent call last):
File "In [17]", line 1, col 1, in 'cdr'
File "In [17]", line 1, col 1
RunTimeError: cdr called on non-pair ()
[18]:
(cdr '((a b c) x y z))
[18]:
(x y z)
[19]:
(define s '((help) this))
(define l '(is very ((hard) to learn)))
(cons s l)
[19]:
(((help) this) is very ((hard) to learn))
S-expression
是什么? - atom
- list
Scheme
里的所有东西都是S-expression
?
https://www.quora.com/In-Scheme-is-everything-an-expression
什么为空, 空的判定:
'()
代表空
(null? ..)
判定是否为空
[20]:
(null? '())
[20]:
#t
(eq? ..)
的定义: 比较两个参数是否相等, 参数必须都是非数值的atom
.
实际中不做这个严格定义.
[21]:
(eq? 1 1)
[21]:
#t
[22]:
(eq? '() '())
[22]:
#t
[23]:
(eq? '(a b) '(a b))
[23]:
#f
[24]:
(eq? 'a 'a)
[24]:
#t
定义(lat? )
方法, 用于判断列表元素是否都是atom
.
用到了递归, 递归的核心是发现一个问题内在的结构, 逐步缩小问题规模, 直到停止条件.
[25]:
(define (lat? l)
(cond
((null? l) #t)
((atom? (car l))
(lat? (cdr l)))
(else #f)))
[26]:
(lat? '(a b c))
[26]:
#t
[27]:
(lat? '((Jack) tom sprat)) ;; '(Jack) 是列表, 不是atom
[27]:
#f
定义(member? a lat)
方法, 用来判断一个atom
是否是一个列表的元素.
[28]:
(define (member? a lat)
(cond
((null? lat) #f)
((eq? a (car lat)) #t)
(else (member? a (cdr lat)))))
[29]:
(member? 'a '(b a c))
[29]:
#t
定义(rember a lat)
方法, 从一个列表中移除一个元素.
cons
加递归, 不断构建新列表的时候, 在原始列表元素等于查询a
时, 跳过它, 从而在最终生成的列表中移除了a
.
这里实际实现的是后面的multirember
方法.
[30]:
(define (rember a lat)
(cond
((null? lat) lat)
((eq? a (car lat)) (rember a (cdr lat)))
(else
(cons (car lat) (rember a (cdr lat))))))
[31]:
(rember 'a '(b a c d))
[31]:
(b c d)
[32]:
(rember 'a '(b a c a d a))
[32]:
(b c d)
定义(first l)
, 从一个元素均为列表的列表中, 提取每个元素的第一个元素, 组成一个新的列表.
缺点是对与(() (..) (..))
之类存在空列表元素的情况没有处理.
这里一个所谓重要”教条”是, cons
构建列表时, 第一个参数是典型基本元素, 第二个参数是一个递归过程.
[33]:
(define (first l)
(cond
((null? l) l)
(else
(cons (car (car l)) (first (cdr l))))))
[34]:
(first '((five plums) (four) (eleven green oranges)))
[34]:
(five four eleven)
[35]:
(cons '() '(1))
[35]:
(() 1)
定义(insertR new old lat)
方法, 在old右侧, 插入new.
两个步骤:
找到old, 找到后插入new, 然后终止递归程序.
构建新列表
[36]:
(define (insertR new old lat)
(cond
((null? lat) lat)
((eq? old (car lat))
(cons
(cons (car lat) new)
(cdr lat)))
(else
(cons (car lat) (insertR new old (cdr lat))))))
[37]:
(insertR 'e 'd '(a b c d f g d h))
[37]:
(a b c (d . e) f g d h)
对应的也能写出insertL
, 略.
数值是什么?
整数
14
-3
实数
3.14159
数值是atom
.
假设我们有两个基本操作add1
, sub1
, 依靠这两个基本操作构建自然数计算系统.
[38]:
(define (add1 x)
(+ x 1))
[39]:
(define (sub1 x)
(- x 1))
[40]:
(zero? 0)
[40]:
#t
[41]:
(define (plus n m)
(cond
((zero? m) n)
(else
(plus (add1 n) (sub1 m)))))
[42]:
(plus 1 3)
[42]:
4
[43]:
(define (minus n m)
(cond
((zero? m) n)
(else
(minus (sub1 n) (sub1 m)))))
[44]:
(minus 3 2)
[44]:
1
plus
, minus
方法都不能处理负数的情况, 终止条件不对.
给出tuple
的定义, 一个元素都是相同类型的列表. 其它语言中, tuple
可能还有不可变等特点, scheme中list本身是不变的, 因此不用强调.
此处tuple
和list
本质上是一类东西, 操作, 和递归条件的判定都是一致的.
定义(addtup tup)
方法, 将tuple中的数字加和.
[45]:
(define (addtup tup)
(cond
((null? tup) 0)
(else
(+ (car tup) (addtup (cdr tup))))))
[46]:
(addtup '(1 2 3 4 5))
[46]:
15
定义乘法.
[47]:
(define (multiply n m)
(cond
((zero? m) 0)
((eq? m 1) n)
(else
(+ n (multiply n (- m 1))))))
[48]:
(multiply 3 5)
[48]:
15
定义(tup+ t1 t2)
方法, 计算两个tuple相加的结果.
[49]:
(define (tup+ t1 t2)
(cond
((null? t1) t2)
((null? t2) t1)
(else
(cons (+ (car t1) (car t2))
(tup+ (cdr t1) (cdr t2))))))
[50]:
(tup+ '(1 2 3 8) '(4 5 6))
[50]:
(5 7 9 8)
[51]:
(define (gt? n m) ;; >
(cond
((zero? n) #f)
((zero? m) #t)
(else (gt? (sub1 n) (sub1 m)))))
[52]:
(gt? 3 3)
[52]:
#f
[53]:
(define (lt? n m)
(cond
((zero? m) #f)
((zero? n) #t)
(else (lt? (sub1 n) (sub1 m)))))
[54]:
(lt? 3 4)
[54]:
#t
[55]:
(define (eq_? n m)
(cond
((gt? n m) #f)
((lt? n m) #f)
(else #t)))
[56]:
(eq_? 3 3)
[56]:
#t
[57]:
(define (pow n m)
(cond
((eq? m 0) 1)
((eq? m 1) n)
(else
(multiply n (pow n (sub1 m))))))
[58]:
(pow 2 3)
[58]:
8
定义除法(divide n m)
[59]:
(define (divide n m)
(cond
((lt? n m) 0)
(else
(add1 (divide (- n m) m)))))
[60]:
(divide 10 3)
[60]:
3
[61]:
(length '(1 2))
[61]:
2
定义函数(eqan? s1 s2)
, 用于添加比较两个数相等的逻辑.
[62]:
(define (eqan? s1 s2)
(cond
((and (number? s1) (number? s2))
(= s1 s2))
((or (number? s1) (number? s2))
#f)
(else
(eq? s1 s2))))
[63]:
(eqan? "1" "1")
[63]:
#t
rember*
表示遍历整个结构, 去掉a
, 而非rember
,只是去掉首层的a
.
对一个子结构递归, 就是深入到了其中.
相比较之前的rember
等递归函数.
检查条件由:
(null? )
(else cdr)
变成了:
(null? )
(atom? car)
(else cdr)
这个结构大体是通用的.
[64]:
(define (rember* a lat)
(cond
((null? lat) lat)
((atom? (car lat))
(cond
((eq? (car lat) a) (rember* a (cdr lat)))
(else
(cons (car lat) (rember* a (cdr lat))))))
(else
(cons
(rember* a (car lat))
(rember* a (cdr lat))))))
[65]:
(rember* 'a '((a b) a b d e a (e a f)))
[65]:
((b) b d e (e f))
[66]:
(define (leftmost lat)
(cond
((atom? (car lat)) (car lat))
(else
(leftmost (car lat)))))
[67]:
(leftmost '((((foo) bar) test) all))
[67]:
foo
回头看如何比较两个列表相等:
都为空, #t
一个为空, #f
第一个元素都是atom
时
有一个第一个元素是atom
, #f
第一个元素都是列表时
[68]:
(define (eqlist? l1 l2)
(cond
((and (null? l1) (null? l2)) #t)
((or (null? l1) (null? l2)) #f)
((and (atom? (car l1)) (atom? (car l2)))
(and (eqan? (car l1) (car l2))
(eqlist? (cdr l1) (cdr l2))))
((or (atom? (car l1)) (atom? (car l2))) #f)
(else
(and
(eqlist? (car l1) (car l2))
(eqlist? (cdr l2) (cdr l2))))))
[69]:
(eqlist? '((1 2) "a") '((1 3) "b"))
[69]:
#f
[70]:
(eqlist? '((1 2) "a") '((1 2) "a"))
[70]:
#t
复习S-expression
的语法定义: atom
或是包含S-expression
的list
.
这种结构定义可以用文字描述, 也可以用BNF
等语言来表达. 根据这个结构, 可以写出对应语法的解析器, 也可以写出一般的功能函数. 这些程序的结构也是类似的, 使用递归自上而下的遍历.
现在定义函数(equal_? s1 s2)
, 用来比较两个S-expression
.
[71]:
(define (equal_? s1 s2)
(cond
((and (atom? s1) (atom? s2))
(eqan? s1 s2))
((or (atom? s1) (atom? s2)) #f)
(else
(eqlist? s1 s2))))
[72]:
(equal_? 'a 'a)
[72]:
#t
[73]:
(equal_? '(a b 1) '(a b 1))
[73]:
#t
首先定义算数表达式
:
atom
使用+
, *
, ^
两两组合的算数表达式.
定义函数(numbered? aexp)
, 用于判断aexp
是否是一个算数表达式.
[74]:
(define (numbered? aexp)
(cond
((atom? aexp) (number? aexp))
(else
(and (numbered? (car aexp))
(numbered? (car (cdr (cdr aexp))))))))
[75]:
(numbered? '(1 + (3 * 2) + a)) ;; 连加/乘的形式不处理
[75]:
#t
定义求值函数
[76]:
(define (value aexp)
(cond
((atom? aexp) aexp)
((eq? (car (cdr aexp)) '+)
(+
(value (car aexp))
(value (car (cdr (cdr aexp))))))
((eq? (car (cdr aexp)) '*)
(*
(value (car aexp))
(value (car (cdr (cdr aexp))))))
((eq? (car (cdr aexp)) '^)
(pow
(value (car aexp))
(value (car (cdr (cdr aexp))))))))
[77]:
(value '(1 + (2 ^ 3)))
[77]:
9
一种新的数字表示形式:
'()
代表0, '(())
代表1, '(() ())
代表2, 依次类推.
四个基本操作
zero?
number?
add1
sub1
就可以构建计算系统, 代码与前面的plus
, minus
, pow
, gt?
, lt?
, eq_?
等类似. 这表现了抽象的威力.
另一种Church Encoding, 也类似, 用Lambda表达式和函数调用层次表示数字.
集合
是什么? 一组互不相同的元素组成的列表.
集合, 元素与集合, 集合与集合.
[78]:
(define (set? lat)
(cond
((null? lat) #t)
((member? (car lat) (cdr lat)) #f)
(else
(set? (cdr lat)))))
[79]:
(set? '(1 2 3))
[79]:
#t
[80]:
(set? '(1 1 2 3 3))
[80]:
#f
复杂度比较高\(O(n^2)\), 使用hashmap
可以降到\(O(n)\), 使用排序, 能到\(O(nlogn)\).
[81]:
(define (makeset lat)
(cond
((null? lat) lat)
((member? (car lat) (cdr lat)) (makeset (cdr lat)))
(else
(cons (car lat) (makeset (cdr lat))))))
[82]:
(makeset '(1 3 2 1 3 4 5))
[82]:
(2 1 3 4 5)
[83]:
(define (subset? s1 s2)
(cond
((null? s1) #t)
((member? (car s1) s2) (subset? (cdr s1) s2))
(else #f)))
[84]:
(subset? '(1 2 4) '(1 2 3))
[84]:
#f
[85]:
(subset? '(1 2) '(1 2 3 4 5))
[85]:
#t
两个集合相等, 当且仅当它们互为对方的子集.
[86]:
(define (eqset? s1 s2)
(and (subset? s1 s2)
(subset? s2 s1)))
[87]:
(eqset? '(1 3 2) '(1 2 3))
[87]:
#t
两个集合相交是指, 存在共有元素. 两个集合的交集是, 两个集合共有元素的集合.
[88]:
(define (intersect? s1 s2)
(cond
((null? s1) #f)
((member? (car s1) s2) #t)
(else
(intersect? (cdr s1) s2))))
[89]:
(intersect? '(1 2 3) '(6 4 5))
[89]:
#f
[90]:
(intersect? '(1 2 3) '(3 4 5))
[90]:
#t
[91]:
(define (intersect s1 s2)
(cond
((null? s1) s1)
((member? (car s1) s2)
(cons (car s1) (intersect (cdr s1) s2)))
(else
(intersect (cdr s1) s2))))
[92]:
(intersect '(1 2 3) '(2 7 9))
[92]:
(2)
[93]:
(intersect '(1 2 3 4 5 6 7 8) '(6 9 11 32 4 55))
[93]:
(4 6)
[94]:
(define (intersectall l-set) ;; 主要是发现问题结构上的特点, 发现结构后, 就比较容易解决
(cond
((null? (cdr l-set)) (car l-set))
(else
(intersect (car l-set)
(intersectall (cdr l-set))))))
[95]:
(intersectall '((1 2 3) (3 4 5 7 7) (3 1 11 34 9)))
[95]:
(3)
并集是两个集合出现的共有元素.
[96]:
(define (union s1 s2)
(cond
((null? s1) s2)
((member? (car s1) s2) (union (cdr s1) s2))
(else
(cons (car s1) (union (cdr s1) s2)))))
[97]:
(union '(1 2 3) '(3 4 5 6))
[97]:
(1 2 3 4 5 6)
关系(rel
)是什么:
一组Pair
组成的列表.
[98]:
(define (a-pair? x)
(cond
((atom? x) #f)
((null? x) #f)
((null? (cdr x)) #f)
((null? (cdr (cdr x))) #t)
(else #f)))
[99]:
(a-pair? '(1 2))
[99]:
#t
函数是什么?
集合A到集合B的多(一)对一映射.
[100]:
(define (firsts lat)
(cond
((null? lat) lat)
(else
(cons (car (car lat))
(firsts (cdr lat))))))
[101]:
(firsts '((1 2) (3 4) (5 7) (8)))
[101]:
(1 3 5 8)
[102]:
(define (fun? rel)
(set? (firsts rel)))
[103]:
(fun? '((1 2) (3 4) (5 6) (7 8)))
[103]:
#t
上面的关系, 就是函数\(f(x)=x+1, x\in\{1, 3, 5, 8\}\).
逆关系(revrel
)是什么?
关系的每个Pair
, 第一个元素和第二个元素互换, 即是.
[104]:
(define (revrel rel)
(cond
((null? rel) rel)
(else
(cons (cons (car (cdr (car rel)))
(car (car rel)))
(revrel (cdr rel))))))
[105]:
(revrel '((1 2) (3 4) (5 6) (7 8)))
[105]:
((2 . 1) (4 . 3) (6 . 5) (8 . 7))
涉及到函数作为值这一点.
定义(rember-f test? a l)
, 将谓词函数作为参数传进去, 而不是写死在代码中.
[106]:
(define rember-f
(lambda (test?)
(lambda (a l)
(cond
((null? l) l)
((test? a (car l)) (rember-f test? a (cdr l)))
(else
(cons (car l) ((rember-f test?) a (cdr l))))))))
[107]:
((rember-f eq?) 'a '(1 2 3))
[107]:
(1 2 3)
(define (f args..) ...)
实际是(define f (lambda (args..) ...))
的语法糖, 而后者, 又是下面代码的语法糖:
(define f
(lambda (a0)
(lambda (a1)
...
(lambda (an)
...))))
f(x0)
就会返回(lambda (a1) ...)
这个partial function, 依次类推, 我们把这个过程叫做curry-ing, 即柯里化.
[108]:
(define rember-eq? (rember-f eq?))
考虑我们之前定义的value
函数:
(define (value aexp)
(cond
((atom? aexp) aexp)
((eq? (car (cdr aexp)) '+)
(+
(value (car aexp))
(value (car (cdr (cdr aexp))))))
((eq? (car (cdr aexp)) '*)
(*
(value (car aexp))
(value (car (cdr (cdr aexp))))))
((eq? (car (cdr aexp)) '^)
(pow
(value (car aexp))
(value (car (cdr (cdr aexp))))))))
其中, 判断算符进行计算的过程, 有一个明显的公共模式. 我们可以建立一个符号->算符的映射函数, 通用的解决它.
[109]:
(define (atom->fun x)
(cond
((eq? x '+) +)
((eq? x '*) *)
((eq? x '^) pow)))
[110]:
(define (value-1 aexp)
(cond
((atom? aexp) aexp)
(else
((atom->fun (car (cdr aexp)))
(value-1 (car aexp))
(value-1 (car (cdr (cdr aexp))))))))
[111]:
(value-1 '(1 + (2 * 3)))
[111]:
7
定义\(eq?-c^1\)函数, (eq?-c1 k), k="salad"
调用的结果是, 一个函数, 判断参数是否eq?
”salad”这个值.
[112]:
(define eq?-c1
(lambda (a)
(lambda (x)
(eq? x a))))
[113]:
(eq?-c1 "salad")
[113]:
#<procedure>
[114]:
((eq?-c1 'salad) 'salad)
[114]:
#t
[115]:
(define eq?-salad (eq?-c1 'salad))
[116]:
(eq?-salad 'salad)
[116]:
#t
[117]:
(define insertL-f
(lambda (test?)
(lambda (new old l)
(cond
((null? l) '())
((test? (car l) old)
(cons new (cons old (cdr l))))
(else
(cons (car l)
((insertL-f test?) new old (cdr l))))))))
[118]:
((insertL-f eq?) 'a 'b '(a b c d e))
[118]:
(a a b c d e)
[119]:
(define insert-g0
(lambda (is-left-side)
(lambda (test?)
(lambda (new old l)
(cond
((null? l) '())
((test? (car l) old)
(cond
((= is-left-side #t)
(cons new (cons old (cdr l))))
(else
(cons old (cons new (cdr l))))))
(else
(cons (car l)
(((insert-g0 is-left-side) test?) new old (cdr l)))))))))
[120]:
(((insert-g0 #t) eq?) 'a 'b '(a b c d e))
[120]:
(a a b c d e)
比较”土”的方式.
定义seqL
, seqR
, 接收(new old l)
, 分别生成:
seqL
new old (cdr l)
seqR
old new (cdr l)
将insert-g
中的is-left-side
开关, 替换成对应的seq
方法.
(define insertL (insert-g seqL))
(define insertR (insert-g seqR))
[121]:
(define (seqL new old l)
(cons new (cons old l)))
[122]:
(define (seqR new old l)
(cons old (cons new l)))
[123]:
(define insert-g
(lambda (seq)
(lambda (new old l)
(cond
((null? l) '())
((eq? (car l) old)
(seq new old (cdr l)))
(else
(cons (car l)
((insert-g seq) new old (cdr l))))))))
[124]:
((insert-g seqL) 'a 'b '(a b c d e))
[124]:
(a a b c d e)
[3]:
(define subst0 ;; 替换一次
(lambda (new old l)
(cond
((null? l) '())
((eq? (car l) old)
(cons new (cdr l)))
(else
(cons (car l)
(subst0 new old (cdr l)))))))
[4]:
(subst0 'a 'b '(a b c d e))
[4]:
(a a c d e)
观察发现, subst0
和insert-g
有着相似的结构. 因此可以重写subst
如下:
[127]:
(define (seqS new old l)
(cons new l))
[128]:
(define subst (insert-g seqS))
[129]:
(subst 'a 'b '(a b c d e))
[129]:
(a a c d e)
上面的rember-f
程序, 实际上是multirember-f
, 略过了rember
的实现.
下面引入continuation
.
[130]:
(define multirember&co
(lambda (a lat col)
(cond
((null? lat)
(col '() '()))
((eq? (car lat) a)
(multirember&co a
(cdr lat)
(lambda (newlat seen)
(col newlat (cons (car lat) seen)))))
(else
(multirember&co a
(cdr lat)
(lambda (newlat seen)
(col (cons (car lat) newlat) seen)))))))
这里的col
的作用, 通过递归的调用, 将中间数据存储在各级的闭包中. col的作用是一个collector
, 一般被称作continuation
.
[131]:
(define (a-friend x y)
(null? y))
[132]:
(multirember&co 'tuna '(strawberries tuna and swordfish) a-friend)
[132]:
#f
展开上面调用的执行过程:
col = (lambda (x y) (null? y))
;; 1)
(m&c 'tuna '(strawberries tuna and swordfish) col)
;; 2) else
(m&c 'tuna
'(tuna and swordfish)
(col (cons 'strawberries x) y))
;; 3) match
(m&c 'tuna
'(and swordfish)
(col (cons 'strawberries x) (cons 'tuna y)))
;; 4) else
(m&c 'tuna
'(swordfish)
(col (cons 'strawberries x) (cons 'and (cons 'tuna y))))
;; 5) else
(m&c 'tuna
'()
(col (cons 'strawberries x) (cons 'swordfish (cons 'and (cons 'tuna y)))))
;; 6) null? #t
(col '() '())
(null?
'(strawberries)
'(swordfish and tuna))
>>> #f
[133]:
(multirember&co 'tuna '() a-friend)
[133]:
#t
[134]:
(multirember&co 'tuna '(tuna) a-friend)
[134]:
#f
;; 1) match
(m&c 'tuna
'()
(col x (cons 'tuna y)))
;; 2) null? #t
(col '() '())
(null?
'()
'(tuna))
>>> #f
回头看, (col x y)
的两个参数, x
是不满足条件的一组集合, y
是满足条件的一组集合. 根据上面的结论, 我们设计cnt-friend
, 分别统计两个集合的大小.
这里的collector
, 类似于reduce
或者fold
.
[135]:
(define (cnt-friend x y)
(cons (length x) (length y)))
[136]:
(multirember&co 'tuna '(strawberries tuna and swordfish) cnt-friend)
[136]:
(3 . 1)
TODO 练习:
multiinsertL/multiinsertR/multiinsertLR
multiinsertLR&co
evens-only*
evens-only*&co
函数looking
, 从输入列表的第一个位置找起, 如果元素是数字, 表示列表索引, 继续查找, 如果非数字, 判断是否等于要找的元素, 是#t
, 否#f
.
[137]:
(define (pick i lat)
(cond
((null? lat) '())
((= i 1) (car lat))
(else
(pick (- i 1) (cdr lat)))))
[138]:
(pick 3 '(1 2 3))
[138]:
3
[139]:
(define (keep-looking a i lat)
(cond
((null? lat) #f)
((number? i)
(keep-looking a (pick i lat) lat))
(else (eq? i a))))
[140]:
(define (looking a lat)
(keep-looking a (pick 1 lat) lat))
[141]:
(looking 'caviar '(6 2 4 caviar 5 7 3))
[141]:
#t
[142]:
(looking 'caviar '(6 2 grits caviar 5 7 3))
[142]:
#f
keep-looking
与以前的递归不同的地方, 在于lat
的数据规模一直没有缩小.
它的结束, 依赖于输入数据, 输入(7 2 4 7 5 6 3)
, 永远停不下来.
total function
对所有输入都能在有限步后给出结果
partial function
只对部分输入能在有限步后给出结果
looking
就是这类函数
[143]:
(define (eternity x) ;; 对任何输入都不能停, 也是一个`partial function`
(eternity x))
[144]:
(define (shift pair)
(build (fst (fst pair))
(build (snd (fst pair))
(snd pair))))
[145]:
(define (fst pair)
(car pair))
[146]:
(define (snd pair)
(cond
[(null? pair) '()]
[else (car (cdr pair))]))
[185]:
(define (third lat)
(car (cdr (cdr lat))))
[147]:
(define (build a b)
(cons a (cons b '())))
[148]:
(shift '((a b) (c d)))
[148]:
(a (b (c d)))
[149]:
(shift '((a b) c))
[149]:
(a (b c))
[150]:
(define (align pora)
(cond
[(atom? pora) pora]
[(a-pair? (fst pora)) (align (shift pora))]
[else (build (fst pora)
(align (snd pora)))]))
[151]:
(align '((a b) (c (d e))))
[151]:
(a (b (c (d e))))
[152]:
(shift '((a b) (c (d e))))
[152]:
(a (b (c (d e))))
[153]:
(define (length* pora)
(cond
[(atom? pora) 1]
[else
(+ (length* (fst pora))
(length* (snd pora)))]))
[154]:
(length* '((a b) (c (d (e f)))))
[154]:
6
[155]:
(define (revpair p)
(build (snd p) (fst p)))
[156]:
(revpair '(a b))
[156]:
(b a)
[157]:
(define (shuffle pora)
(cond
[(atom? pora) pora]
[(a-pair? (fst pora))
(shuffle (revpair pora))]
[else
(build (fst pora)
(shuffle (snd pora)))]))
[158]:
(shuffle '(a (b c)))
[158]:
(a (b c))
[159]:
(shuffle '(a b))
[159]:
(a b)
(shuffle '((a b) (c d)))
不能结束, shuffle
函数是partial
的.
[160]:
(define (C n)
(cond
[(= n 1) 1]
[(even? n) (C (/ n 2))]
[else
(C (add1 (* 3 n)))]))
;; (C 0) 不能结束, partial的
[161]:
(define (A n m)
(cond
[(zero? n) (add1 m)]
[(zero? m) (A (sub1 n) 1)]
[else (A (sub1 n) (A n (sub1 m)))]))
[162]:
(A 1 0)
[162]:
2
[163]:
(A 2 2)
[163]:
7
Ackermann function
,
停机判定问题, 这里will-stop?
函数是定义不出来的, 因为围绕它的假设, 可以构造出一个悖论来.停机问题是遇到的第一个可以被精确描述, 但是不能在程序中定义的问题.
图灵停机问题和哥德尔不完备定理.
停机问题涉及自我指涉
(递归就是种自指), 本质是一阶逻辑的不自洽和不完备.
回头看length
函数:
(define (length lat)
(cond
[(null? lat) 0]
[else (add1 (length (cdr lat)))]))
我们不再使用define
绑定, 定义一个只能处理空列表的\(length_0\)(输入非空因为``eternity``的原因, 会不”停机”).
(lambda (lat)
(cond
[(null? lat) 0]
[else (add1 (eternity (cdr lat)))]))
利用\(length_0\), 我们可以定义一个能处理空列表, 单元素列表的\(length_{\leq1}\):
(lambda (lat)
(cond
[(null? lat) 0]
[else (add1 (length0 (cdr l)))]))
不存在\(length_0\)绑定, 所以展开.
scheme (lambda (lat) (cond [(null? lat) 0] [else (add1 (lambda (lat) (cond [(null? lat) 0] [else (add1 (eternity (cdr lat)))])) (cdr lat)]))
同理, 我们用相同的结构不断替换eternity
,就可以得到不同的\(\leqN\)版本length
函数. 那么能否定义一个无限的函数, 来求解\(length_{\leq\infty}\)? 不能. 但是存在的这个函数的重复结构, 怎样表达?
((lambda (length)
(lambda (lat)
(cond
[(null? lat) 0]
[else (add1 (length (cdr lat)))])))
eternity)
按这种形式重写\(length_{\leq1}\),
((lambda (f)
(lambda (lat)
(cond
[(null? lat) 0]
[else (add1 (f (cdr lat)))])))
((lambda (g)
(lambda (lat)
(cond
[(null? lat) 0]
[else (add1 (g (cdr lat)))]))))
eternity))
定义\(length_{\leq2}\)还需要再加一组(lambda (k) ...)
, 还是代码上重复. 怎么办? 定义一个函数, 输入是length
, 输出还是length
.
定义\(length_0\):
((lambda (mk-length)
(mk-length eternity))
(lambda (length)
(lambda (lat)
(cond
[(null? lat) 0]
[else (add1 (length (cdr lat)))]))))
定义\(length_{\leq1}\):
((lambda (mk-length)
(mk-length
(mk-length eternity)))
(lambda (length)
(lambda (lat)
(cond
[(null? lat) 0]
[else (add1 (length (cdr lat)))]))))
如此类推, 只需增加(mk-length (mk-length ...)
层次, 就可以表示函数的反复.
application-order Y combinator
:
[1]:
(define Y
(lambda (le)
((lambda (f) (f f))
(lambda (f)
(le (lambda (x) ((f f) x)))))))
entry
是这样一个pair
:
每个元素都是长度相同的list
第一个列表是set
[208]:
(define new-entry build)
[164]:
(define (lookup-in-entry name entry entry-f)
(lookup-in-entry-help name (fst entry) (snd entry) entry-f))
[165]:
(define (lookup-in-entry-help name names values entry-f)
(cond
[(null? names) (entry-f name)]
[(eq? (car names) name) (car values)]
[else
(lookup-in-entry-help name
(cdr names)
(cdr values)
entry-f)]))
[166]:
(lookup-in-entry 'tom '((lucy lily tom) (girl girl boy)) (lambda (x) x))
[166]:
boy
[167]:
(define extend-table cons)
[168]:
(define (lookup-in-table name table table-f)
(cond
[(null? table) (table-f name)]
[else
(lookup-in-entry name
(car table)
(lambda (name)
(lookup-in-table name
(cdr table)
table-f)))]))
找出值的类型, 定义expr->action
, atom->action
方法.
[169]:
(define (expr->action e)
(cond
[(atom? e) (atom->action e)]
[else (list->action e)]))
[172]:
(define (atom->action e)
(cond
[(number? e) *const]
[(eq? e #t) *const]
[(eq? e #f) *const]
[(eq? e 'cons) *const]
[(eq? e 'car) *const]
[(eq? e 'cdr) *const]
[(eq? e 'null?) *const]
[(eq? e 'eq?) *const]
[(eq? e 'atom?) *const]
[(eq? e 'zero?) *const]
[(eq? e 'add1) *const]
[(eq? e 'sub1) *const]
[(eq? e 'number?) *const]
[else *identifier]))
[173]:
(define (list->action e)
(cond
[(atom? (car e))
(cond
[(eq? (car e) 'quote) *quote]
[(eq? (car e) 'lambda) *lambda]
[(eq? (car e) 'cond) *cond]
[else *application])]
[else *application]))
[174]:
(define (meaning e table)
((expr->action e) e table))
[175]:
(define (value e) (meaning e '()))
[177]:
(define (*const e table) ;; action for constants
(cond
[(number? e) e]
[(eq? e #t) #t]
[(eq? e #f) #f]
[else (build 'primitive e)]))
[180]:
(define (*quote e table) ;; action for (quote ..)
(text-of e))
[181]:
(define text-of snd)
[182]:
(define (*identifier e table)
(lookup-in-table e table initial-table))
[183]:
(define (initial-table name) ;; 不应该被用到
(car '()))
primitive
和non-primitive
的区别, non-primitive
需要记住它的形参和body.
(non-primitive
( (((y z) ((8) 9))) (x) (cons x y) ))
|--- table ----| |- formals -| |-- body --|
[184]:
(define (*lambda e table)
(build 'non-primitive
(cons table (cdr e))))
[186]:
(define table-of fst)
[187]:
(define formals-of snd)
[188]:
(define body-of third)
定义cond
语句, 首先定义一组辅助函数.
[190]:
(define (else? x)
(cond
[(atom? x) (eq? x 'else)]
[else #f]))
[191]:
(define question-of fst)
[192]:
(define answer-of snd)
[193]:
(define (evcon lines table)
(cond
[(else? (question-of (car lines)))
(meaning (answer-of (car lines)) table)]
[(meaning (question-of (car lines)) table)
(meaning (answer-of (car lines)) table)]
[else (evcon (cdr lines) table)]))
[194]:
(define cond-lines-of cdr)
[195]:
(define (*cond e table)
(evcon (cond-lines-of e) table))
*application
的结构:
一个列表
第一个元素的值是一个函数
apply时,所有参数的值需要求到
定义evlis
(eval list), 输入一个列表和table, 得到所有值组成列表.
然后得到function
的值, (meaning-of-function meaning-of-args)
得到这个*application
的值.
[196]:
(define (evlis args table)
(cond
[(null? args) '()]
[else
(cons (meaning (car args) table)
(evlis (cdr args) table))]))
[197]:
(define function-of car)
[198]:
(define arguments-of cdr)
[199]:
(define (*application e table)
(apply (meaning (function-of e) table)
(evlis (arguments-of e) table)))
程序中包含2种函数:
(primitive name)
(non-primitive (table formals body))
这里(table formals body)
被称作一条闭包记录(closure record
)
[200]:
(define (primitive? l)
(eq? (fst l) 'primitive))
[201]:
(define (non-primitive? l)
(eq? (fst l) 'non-primitive))
求值函数
[202]:
(define (apply fun vals)
(cond
[(primitive? fun)
(apply-primitive (snd fun) vals)]
[(non-primitive? fun)
(apply-closure (snd fun) vals)]))
[204]:
(define (apply-primitive name vals)
(cond
[(eq? name 'cons) (cons (fst vals) (snd vals))]
[(eq? name 'car) (car (fst vals))]
[(eq? name 'cdr) (cdr (fst vals))]
[(eq? name 'null?) (null? (fst vals))]
[(eq? name 'eq?) (eq? (fst vals) (snd vals))]
[(eq? name 'atom?) (:atom? (fst vals))]
[(eq? name 'zero?) (zero? (fst vals))]
[(eq? name 'add1) (add1 (fst vals))]
[(eq? name 'sub1) (sub1 (fst vals))]
[(eq? name 'number?) (number? (fst vals))]))
[205]:
(define (:atom? x)
(cond
[(atom? x) #t]
[(null? x) #f]
[(eq? (car x) 'primitive) #t]
[(eq? (car x) 'non-primitive) #f]
[else #f]))
[207]:
(define (apply-closure closure vals)
(meaning (body-of closure)
(extend-table
(new-entry (formals-of closure) vals)
(table-of closure))))
[210]:
(meaning '(cons z x) '(((x y)
((a b c) (d e f)))
((u v w)
(1 2 3))
((x y z)
(4 5 6))))
[210]:
(6 a b c)
[211]:
(apply '(primitive cons) '(6 (a b c)))
[211]:
(6 a b c)
(完)