# 斯坦福编程范式 CS107_23
# Scheme 内存模型
>'(1 2 3) |
对于上述的数据,他的内存结构如下所示。

当使用以下命令时,scheme 会创建一个全局符号表,这个东西交 seq ,它会与这个变量关联起来。
>(define seq '(1 2 3)) |

当使用 cons 命令时,如下所示。cons 在合并两个列表的时候其实是构造了一个新列表,如下图所示。
>(cons '(1 2 3) '(4 5 6)) |

因此,输出的结果是如下所示的。
((1 2 3) 4 5 6) |
下面让我们用两种方式构建一个列表:
((1 2) 1 2) |
# 第一种方法
>(cons '(1 2) '(1 2)) |
这种方法和上面描述 cons 的使用大差不差,我们主要来介绍第二种方法。
# 第二种方法
使用如下内存存储方式:

如果想要构造如上所示的列表,那么我们要用到一点技巧。
((lambda(x) (cons x x)) '(1 2)) |
通过上述代码我们构造了一个列表,并且将这个列表绑定到另一个上面,该变量在该定义中用到两次。最终使 car 和 cdr 域都指向同一个内容。
# 改造我们之前的只能处理一元的 map
先把之前的那个一元的 map 重写一遍:
(define (unary-map fn seq) | |
(if (null? seq) () | |
(cons (fn (car seq)) | |
(unary-map fn | |
(cdr seq))))) |
为了使这个 map 能够容纳多个参数,我们需要使用 ... 在 scheme 中。
如下例子所示,这个函数 bar 在接受完参数 a、b、c 之后,剩下的所有参数都需要以列表的形式存放在参数 d 中再传入给函数。随后我们再用 list 将这些参数存放在一个列表中,我们看看随意调用会发生什么。
(define (bar a b c . d) | |
(list a b c d)) | |
>(bar 1 2 3 4 5 6) | |
(1 2 3 (4 5 6)) | |
>(bar 1 2 3) | |
(1 2 3 ()) |
这个例子显示出点在 scheme 中的具体用法。
下面我们来构造我们的一般性的 map 函数,它会需要用到我们之前的一元参数的 map 函数,所以我们在开头提前把它写了出来。
# map 的实现思想
我们最终的 map 要实现 scheme 中一模一样的功能:
>(map car '((1 2) (3 4) (5 6 7))) | |
(1 3 5) | |
>(map + '(1 2) '(10 20) '(100 400)) | |
(111 422) | |
>(map * '(1) '(2) '(3) '(4) '(5)) | |
(120) |
简单起见。我们先假设,first-list 这一列表,与 other-lists 里面的所有列表长度相同。
(define (map fn first-list . other-lists) | |
(if (null? first-list) '() | |
(cons (apply fn (cons (car first-list) (unary-map car other-lists))) | |
(apply map | |
(cons fn | |
(cons (cdr first-list) | |
(unary-map cdr other-lists)))) |
我们来举一个例子来对上述复杂的代码进行测试。
>(map list (1 10 100) (2 20 200) (3 30 300)) |
第一次调用第一个接收第一块数据 (1 10 100) 将它绑定到 first-list 上;其他的 ((2 20 200) (3 30 300)) 都绑定到 other-lists 上; 使用 unary-map list other-lists 我们得到了 (2 3) ;随后是 cons (car first-list) (unary-map list other-lists) 得到了 (1 2 3) ;随后我们对上述结果使用 apply fn 即 apply list 最终得到了 ((1 2 3)) 。
接下来递归的部分,对 other-lists 的 cdr 部分,即 ((20 200) (30 300)) 进行 map,之后我们使用 cons (cdr first-list) 把第一个列表中的 cdr 部分加在它们前面;得到 ((10 100) (20 200) (30 300)) ;然后再把 fn 放在这整个部分的最前面,得到 (list (10 100) (20 200) (30 300)) ;最后再使用 apply map 对这整个部分进行递归计算,即 (map list (10 100) (20 200) (30 300)) 。
# Scheme 中的内存创建或释放
假设我们执行如下所示的代码:
(define x '(1 2 3)) | |
(define y (cdr x)) |
在执行第一行代码的时候,列表 (1 2 3) 被绑定到 x 上,内存结构如下:

执行第二行代码的时候, cdr x 被绑定到 y 上,即内存结构如下所示:

当我想将 x 的值换一下的时候:
(define x '(4 5)) |
内存结构如下:

y 的值不会改变,它仍然指向 '(2 3) ,这个时候 1 就成了没有使用的垃圾内存。
系统会为这块无用内存保留一段时间,它可能在等待 IO,也可能是在等待网络连接等等,当它没有任务的时候,它就会前往垃圾收集站,去回收一些没用的内存。