# 斯坦福编程范式 CS107_21

# 将之前 C++ 写的一个函数用 Scheme 编程

使用 scheme 实现如下内容:

>(double-all '(1 2 3 4))
(2 4 6 8)
>(incr-all '(1 2 3 4))
(2 3 4 5)

对于这种功能,不需要迭代,只需要如下代码就可以实现:

(define (double x)(* x 2))
(define (incr x)(+ x 1))

在 scheme 中还有一个内置函数被称为 Map,它可以接收 2 个或多个参数,并且它的第一个参数可以是之前定义好的函数的名字,第二个参数是你要连接的列表。如下所示。map 是递归的对参数列表中的每一个元素进行相应的函数操作。

>(map double '(1 2 3 4))
(2 4 6 8)
>(map incr '(1 2 3 4))
(2 3 4 5)
>(map car '((1 2) (4 8 2) (11)))
(1 4 11)
>(map cdr '((1 2) (4 8 2) (11)))
((2) (8 2) ())

使用 map 函数处理多元参数的函数。如 cons,那么我们需要提供两个列表,第一个列表中的元素都是 cons 的第一个参数,第二个列表中的元素都是 cons 的第二个参数。

>(map cons '(1 2 8) '((4) () (2 5)))
((1 4) (2) (8 2 5))
>(map + '(1 2) '(3 4) '(6 10))
(10 16)

假设你传入的参数比如列表长度不一致,map 也不会生气,它会在最短的对齐结束后就结束运行。

>(map + '(1 2) '(3 4 1) '(6 10))
(10 16)

# 下面来实现这样的一个 map 函数

(define (my-unary-map fn seq)
  (if (null? seq) '()
      (cons (fn (car seq))
            (my-unary-map fn (cdr seq)))))

# eval 函数和 apply 函数

# eval

eval 函数是每次你写好 scheme 语句后,敲回车都会执行的函数。它的作用如下所示。

>'(+ 1 2 3)
(+ 1 2 3)
>(eval '(+ 1 2 3))
6

eval 的应用场景一般是 apply 无法使用的时候,apply 后面无法跟 define、and、or 这一类特殊的函数。

# apply

apply 和 eval 的区别在于,apply 允许你指定用哪一个函数来处理后面的那些参数。

>(apply + '(1 2 3))
6

让我们使用 apply 写一个计算所有数字平均值的函数。假设都是 double 类型的,那样就不需要去处理小数点之类的问题:

(define (average num-list)
  (/ (apply + num-list)
     (length num-listt)))

# 使用 map 和 apply 来消除嵌套列表问题

((1 2) ((3) ((4) 5)) 10)

如果传入的参数不是一个列表,那么我们就把它单独放在一个列表中,便于后面我们使用 append 对所有列表进行拼接。后面的是一个递归使用,其中 map flatten seq 递归到最后应该是多个列表,我们再使用 apply 强行将 append 安排在这些列表的最前面,形成了 append '(1 2) '(3 4 5) '(10) 这样的结构,最终合成一个列表 '(1 2 3 4 5 10)

(define (flatten seq)
  (if(not (list? seq) (list seq))
     (apply append
            (map flatten seq))))

# 构建一个广义翻译的函数

作用:我们希望它能接收一个参数长度为 n 的一个列表,然后产生另一个 n 个点的列表,每个点都偏移了 delta 的量。

>(translate '(2 5 8 11 25) 100)
(102 105 108 111 125)

构造:通过 lambda 构造了一个匿名函数,在这个函数中,对 points 的每一个元素都 + delta 值。lambda 函数已经很熟悉了,可以参考知乎的文章。

(define (translate points delta)
  (map (lambda(x) (+ x delta)) ponits))

上述函数等价于

(define (translate seq delta)
  (define (shift-by x)
    (+ x delta))
  (map shift-by seq))

lambda 函数的简单例子和它的等价形式:在使用 lambda 函数后,我们就可以更清晰的明白 sum 实际上是可以直接替换成 lambda(x y)(+ x y) 的,而这也是 scheme 中其他函数在被使用时真正的使用过程。即简单的符号替换。

(define (sum x y)
  (+ x y))
==================
(define sum
  (lambda(x y)(+ x y)))

Scheme 语言全是关于符号的,符号评估,函数评估。