scheme语言直译为汉语(十六)

一、从序列中提取需要的多元组

首先先看一个实例问题:给定了自然数n,找出所有不同的有序对i和j,其中 1 ≤ j < i ≤ n 1 \leq j < i \leq n 1j<in,使得 i + j i + j i+j是素数。例如,假定n是6,满足条件的序对就是:
在这里插入图片描述
完成这一计算的一种很自然的组织方式:首先生成出所有小于等于n的正自然数的有序对;而后通过过滤,得到那些和为素数的有序对;最后对每个通过了过滤的序对(i, j), 产生出一个三元组(i, j, i+j)。

那么要完成上面的工作,我们其实需要进行嵌套地枚举,对于i,我们用(enumerate-interval 1 n)枚举出所有从1到n的元素;这之后,对于每个枚举出来的元素,我们枚举出从1到比它们每个元素本身小1的元素,从而获得元素j。再把枚举出的j和它们对应的i用(list i j)组装起来,把组装成的有序对作为元素用append依次合并,就能产生出所需的有序对序列了。

(accumulate append
            nil
            (map (lambda (i)
                   (map (lambda (j) (list i j))
                        (enumerate-interval 1 (- i 1))))
                 (enumerate-interval 1 n)))

尝试把上述过程直译为汉语:

(顺序执行 合并
         空值
         (对序列中的每一项 (规定 (甲)
                           (对序列中的每一项 (规定 (乙) (序列 甲 乙))
                                          (所有元素 1 (- 甲 1))))
                         (所有元素 1 n)))

由于在这类程序里常要用到映射,并用append做累积,我们将它独立出来定义为一个过程:

(define (flatmp proc seq)
    (accumulate append nil (map proc seq)))

尝试把上述过程直译为汉语:

(定义 (从序列中获取所需要的多元组 执行获取过程 序列)
    (顺序执行 合并 空值 (对序列中的每一项 执行获取过程 序列)))

现在可以过滤这一序对的序列,找出那些和为素数的序对。对序列里的每个元素调用过滤谓词。由于这个谓词的参数是一个序对,所以它必须将两个整数从序对里提取出来。这样,作用到序列中每个元素上的谓词就是:

(define (prime-sum? pair)
    (prime? (+ (car pair) (cadr pair))))

尝试把上述过程直译为汉语:

(定义 (和是质数? 有序对)
    (是质数? (+ (第一项 有序对) (第二项 有序对))))

最后还要生成出结果的序列,为此只需将下面过程映射到通过过滤后的序对上,对每个有序对里的两个元素,这一过程生成出一个包含了它们的和的三元组:

(define (make-pair-sum pair)
    (list (car pair) (cadr pair) (+ (car pair) (cadr pair))))

尝试把上述过程直译为汉语:

(定义 (创建含有序对两元素及两元素和的三元组 元序对)
    (序列 (第一项 元序对) (第二项 元序对) (+ (第一项 元序对) (第二项 元序对))))

将所有这些组合到一起,就得到了完整的过程:

(define (prime-sum-pairs n)
    (map make-pair-sum
         (filter prime-sum?
                 (flatmap
                  (lambda (i)
                    (map (lambda (j) (list i j))
                         (enumerate-interval 1 (- i 1))))
                     (enumerate-interval 1 n)))))

尝试把上述过程直译为汉语:

(定义 (和为质数的两个数及该质数和 某个数)
    (对序列中的每一项 创建含有序对两元素及两元素和的三元组
         (筛选 和是质数?
               (从序列中获取所需的多元组
               (规定 (甲)
                 (对序列中的每一项 (规定 (乙) (序列 甲 乙))
                                 (所有元素 1 (- 甲 1))))
                 (所有元素 1 某个数)))))

嵌套的映射不仅能用于枚举这种区间,也可用于其他序列。假设我们希望生成集合S的所有排列,也就是说,生成这一集合的元素的所有可能排序方式。例如,{1, 2, 3}的所有排列是{1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2}和{3, 2, 1}。这里是生成S所有排列的序列的一种方案:对于S里的每个x,递归地生成S-x的所有排列的序列,而后将x加到每个序列的前面。这样就能对S里的每个x,产生出了S的所有以x开头的排列。将对所有x的序列组合起来,就可以得到S的所有排列。

(define (permutation s)
    (if (null? s)
        (list nil)
        (flatmap (lambda (x)
                   (map (lambda (p) (cons x p))
                        (pernutations (remove x s))))
                  s)))

尝试把上述过程直译为汉语:

(定义 (所有排列 集合)
    (如果 (到空值了? 集合)
          (序列 空值)
          (从序列中获取所需要的多元组 (规定执行 (元素)
                                    (对序列中的每一项 (规定执行 (余下元素所组成集合的排列) (序对 元素 余下元素所组成集合的排列))
                                                            (所有排列 (去除 元素 集合))))
                                  集合)))

请注意这里所用的策略,看看它如何将生成S的所有排列的问题,归结为生成元素少于S的集合的所有排列的问题。在终极情况中我们将达到空表,它表示没有元素的集合。对此我们生成出的就是(list nil),这是一个只包含一个元素的序列,其中是一个没有元素的集合。在permutations过程中所用的remove过程返回除指定项之外的所有元素,它可以简单地用一个过滤器表示:

(define (remove item sequence)
    (filter (lambda (x) (not (= x item)))
            sequence))
(定义 (去除 指定值 序列)
    (筛选 (规定 (序列元素) (不含 (= 序列元素 指定值)))
            序列))

练习2.40
请定义过程unique-pairs,给它整数n,它产生出序对(i,j),其中1≤j<i≤n。请用unique-pairs去简化上面prime-sum-pairs的定义。
解:

(define (accumulate op initial sequence)
    (if (null? sequence)
        initial
        (op (car sequence)
            (accumulate op initial (cdr sequence)))))
(define (enumerate-interval low high)
    (if (> low high)
        '()
        (cons low (enumerate-interval (+ low 1) high))))
(define (filter predicate sequence)
    (cond ((null? sequence) '())
          ((predicate (car sequence))
           (cons (car sequence)
                 (filter predicate (cdr sequence))))
           (else (filter predicate (cdr sequence)))))

(define (square x)
    (* x x))
(define (smallest-divisor n)
    (find-divisor n 2))
(define (find-divisor n test-divisor)
    (cond ((> (square test-divisor) n) n)
          ((divides? test-divisor n) test-divisor)
          (else (find-divisor n (+ test-divisor 1)))))
(define (divides? a b)
    (= (remainder b a) 0)) 
(define (prime? n)
    (= n (smallest-divisor n)))

(define (unique-pairs n)
    (accumulate append 
                '()
                (map (lambda (i)
                    (map (lambda (j) (list i j))
                        (enumerate-interval 1 (- i 1))))
                    (enumerate-interval 1 n))))
(define (prime-sum? pair)
    (prime? (+ (car pair) (cadr pair))))
(define (make-pair-sum pair)
    (list (car pair) (cadr pair) (+ (car pair) (cadr pair))))
(define (prime-sum-pairs n)
    (let ((the-pairs (filter prime-sum? (unique-pairs n))))
        (map (lambda (pair) (make-pair-sum pair)) the-pairs)))
(display (prime-sum-pairs 6))
(exit)

现在能够编译通过的中文作为变量名及方法名的一个版本:

(define (顺序执行 二元操作 初始值 序列)
    (if (null? 序列)
        初始值
        (二元操作 (car 序列)
                 (顺序执行 二元操作 初始值 (cdr 序列)))))
(define (所有元素 最小值 最大值)
    (if (> 最小值 最大值)
        '()
        (cons 最小值 (所有元素 (+ 最小值 1) 最大值))))
(define (筛选 元素符合条件 序列)
    (cond ((null? 序列) '())
          ((元素符合条件 (car 序列))
           (cons (car 序列)
                 (筛选 元素符合条件 (cdr 序列))))
           (else (筛选 元素符合条件 (cdr 序列)))))

(define (平方 元)
    (* 元 元))
(define (除一外的最小因数 元)
    (找寻最小因数 元 2))
(define (找寻最小因数 元 除数)
    (cond ((> (平方 除数) 元) 元)
          ((整除? 除数 元) 除数)
          (else (找寻最小因数 元 (+ 除数 1)))))
(define (整除? 除数 被除数)
    (= (remainder 被除数 除数) 0)) 
(define (是质数? 元)
    (= 元 (除一外的最小因数 元)))

(define (前项大于后项的所有序对 上限)
    (顺序执行 append 
             '()
             (map (lambda (甲)
                  (map (lambda (乙) (list 甲 乙))
                       (所有元素 1 (- 甲 1))))
                  (所有元素 1 上限))))
(define (和是质数? 序对)
    (是质数? (+ (car 序对) (cadr 序对))))
(define (创建包含序对两元素及两元素和的三元组 序对)
    (list (car 序对) (cadr 序对) (+ (car 序对) (cadr 序对))))
(define (包含和为质数的序对及该质数和的三元组的集合 上限)
    (let ((和是质数的序对 (筛选 和是质数? (前项大于后项的所有序对 上限))))
        (map (lambda (序对) (创建包含序对两元素及两元素和的三元组 序对)) 和是质数的序对)))
(display (包含和为质数的序对及该质数和的三元组的集合 6))
(exit)

在这里插入图片描述
根据希望尝试把上述过程进一步汉化:

(定义 (顺序执行 二元操作 初始值 序列)
    (如果 (到空值了? 序列)
          初始值
          (二元操作 (前头的项 序列)
                   (顺序执行 二元操作 初始值 (后面的项 序列)))))
(定义 (所有元素 最小值 最大值)
    (如果 (> 最小值 最大值)
          空值
          (序对 最小值 (所有元素 (+ 最小值 1) 最大值))))
(定义 (筛选 元素符合条件 序列)
    (情况符合 ((到空值了? 序列) 空值)
             ((元素符合条件 (前头的项 序列))
              (序对 (前头的项 序列)
                    (筛选 元素符合条件 (后面的项 序列))))
             (其它情况 (筛选 元素符合条件 (后面的项 序列)))))

(定义 (平方 元)
    (* 元 元))
(定义 (除一外的最小因数 元)
    (找寻除一外的最小因数 元 2))
(定义 (找寻除一外的最小因数 元 除数)
    (序对 ((> (平方 除数) 元) 元)
          ((整除? 除数 元) 除数)
          (其它情况 (找寻除一外的最小因数 元 (+ 除数 1)))))
(定义 (整除? 除数 被除数)
    (= (取余数 被除数 除数) 0)) 
(定义 (是质数? 元)
    (= 元 (除一外的最小因数 元)))

(定义 (前项大于后项的所有两元组 上限)
    (顺序执行 合并 
             空值
             (对序列中的每一项 (规定 (甲)
                  (对序列中的每一项 (规定 (乙) (序列 甲 乙))
                       (所有元素 1 (- 甲 1))))
                  (所有元素 1 上限))))
(定义 (和是质数? 两元组)
    (是质数? (+ (第一项 两元组) (第二项 两元组))))
(定义 (创建包含两元组及其和的三元组 两元组)
    (序列 (第一项 两元组) (第二项 两元组) (+ (第一项 两元组) (第二项 两元组))))
(定义 (包含和为质数的两元组及该质数和的三元组的集合 上限)
    (命 ((和是质数的两元组的序列 (筛选 和是质数? (前项大于后项的所有两元组 上限))))
        (对序列中的每一项 (规定 (两元组) (创建包含两元组及其和的三元组 两元组)) 和是质数的两元组的序列)))
(输出 (包含和为质数的序对及该质数和的三元组的集合 6))
(退出)

练习2.41
请写出一个过程,它能产生出所有小于等于给定整数n的正的相异整数i、j和k的有序三元组,使每个三元组的三个元之和等于给定的整数s。

解:

(define (filter predicate sequence)
    (cond ((null? sequence) '())
          ((predicate (car sequence))
           (cons (car sequence)
                 (filter predicate (cdr sequence))))
           (else (filter predicate (cdr sequence)))))
(define (accumulate op initial sequence)
    (if (null? sequence)
        initial
        (op (car sequence)
            (accumulate op initial (cdr sequence)))))
(define (flatmap proc seq)
    (accumulate append '() (map proc seq)))
(define (enumerate-interval low high)
    (if (> low high)
        '()
        (cons low (enumerate-interval (+ low 1) high))))

(define (sum-equal-s? s group)
    (= s (+ (car group) (cadr group) (caddr group))))
(define (remove item sequence)
    (filter (lambda (x) (not (= x item)))
            sequence))
(define (unique-pairs n)
    (flatmap (lambda (i)
        (map (lambda (j) (list i j))
            (enumerate-interval 1 (- i 1))))
        (enumerate-interval 1 n)))
(define (unique-triples n)
    (flatmap (lambda (i)
                (map (lambda (j)
                        (cons i j))
                     (unique-pairs (- i 1))))
             (enumerate-interval 1 n)))

(define (permutations s)
    (if (null? s)
        (list '())
        (flatmap (lambda (x)
                    (map (lambda (p) (cons x p))
                         (permutations (remove x s))))
                 s)))
(define (all-triples n)
    (map (lambda (item) (permutations item)) (unique-triples n)))

(define (每项不重复的三元组的集合 几以内)
    (accumulate append '() (all-triples 几以内)))

(define (三个元之和等于s的三元组 s 几以内)
    (filter (lambda (三元组) (sum-equal-s? s 三元组))(每项不重复的三元组的集合 几以内)))
(display (三个元之和等于s的三元组 10 6))
(exit)

在这里插入图片描述

(写代码也中英夹杂一下。。。贼爽,很大程度地解决了我因为英语水平不够造成的命名焦虑问题啊)
上面这段以后再试着直译吧,想先往后写题啦。

练习2.42
“八皇后谜题”问的是怎样将八个皇后摆在国际象棋盘上,使得任意一个皇后都不能攻击另一个皇后(也就是说,任意两个皇后都不在同一行、同一列或者同一对角线上)。一个可能的解如图2-8所示。解决这一谜题的一种方法按-一个 方向处理棋盘,每次在每一列里放一个皇后。如果现在已经放好了k-1个皇后,第k个皇后就必须放在不会被已在棋盘上的任何皇后攻击的位置上。我们可以递归地描述这一过程:假定我们已经生成了在棋盘的前k-1列中放置k-1个皇后的所有可能方式,现在需要的就是对于其中的每种方式,生成出将下一个皇后放在第k列中每一行的扩充集合。而后过滤它们,只留下能使位于第k列的皇后与其他皇后相安无事的那些扩充。这样就能产生出将k个皇后放置在前k列的所有格局的序列。继续这一过程,我们将能产生出这一谜题的所有解,而不是一个解。

将这一解法实现为一个过程queens,令它返回在n * n棋盘上放n个皇后的所有解的序列。queens内部的过程queen-cols,返回在棋盘的前k列中放皇后的所有格局的序列。
在这里插入图片描述

(define (queens board-size)
    (define (queen-cols k)
        (if (= k 0)
            (list empty-board)
            (filter
             (lambda (positions) (safe? k positions))
             (flatmap
              (lambda (rest-of-queens)
                  (map (lambda (new-row)
                          (adjoin-position new-row k rest-of-queens))
                       (enumerate-interval 1 board-size)))
              (queen-cols board-size))))))

这个过程里的rest-of-queens是在前k-1列放置k-1个皇后的一种方式,new-row是在第k列放置所考虑的行编号。请完成这一程序,为此需要实现一种棋盘格局集合的表示方式;还要实现过程adjoin-position,它将一个新的行列格局加入一个格局集合; empty-board,它表示空的格局集合。你还需要写出过程safe?,它能确定在一个格局中,在第k列的皇后相对于其他列的皇后是否为安全的( 请注意,我们只需检查新皇后是否安全一其他皇后已经保证相安无事了)。

解:
有点难,网上看了些答案,也尚未完全理解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X-jazz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值