sicp-mit-11 流

#lang sicp
;;https://www.bilibili.com/video/BV1Xx41117tr
;;https://github.com/DeathKing/Learning-SICP/tree/master/SrtCN
;;第十一节


;;;我们不仅要关注值,也要关注时序
;;;we have to think about not only about values, but about time

;;计算一棵树种奇数的平方和
(define (sum-odd-squares tree)
  (if (leaf-node? tree)
      (if (odd? tree)
          (square tree)
          0)
      (+ (sum-odd-squares (left-branch tree))
         (sum-odd-squares (right-branch tree)))))


;;寻找前n个斐波那契数中的奇数
(define (odd-fibs n)
  (define (next k)
    (if (> k n)
        '()
        (let ((f (fib k)))
          (if (odd? f)
              (cons f (next (1+ k)))
              (next (1+ k))))))
  (next 1))


;;signal processing
;;;enumeriters leaves ---> filter odd? ---> map square ---> acc + 0
;;;enum interval -----> map fib ---> filter odd? ----> acc cons '()

;;;going back to this fundamental principle of computer science
;;;that in order to control something, you need the name of it

;;;流是一种数据抽象 stream is a data abstraction
;;;既然是一种数据抽象,所以先说明它的构造函数和选择函数是什么
(cons-stream x y)
(head s)
(tail s)
(head (cons-stream x y)) --> x
(tail (cons-stream x y)) --> y
;;;the-empty-stream


;;;abstraction layer
;;;abstraction barrier


(define (map-stream proc s)
  (if (empty-stream? s)
      the-empty-stream
      (cons-stream (proc (head s))
                   (map-stream proc (tail s)))))

(define (filter pred s)
  (cond ((empty-stream? s) the-empty-stream)
        ((pred (head s)) (cons-stream (head s) (filter pred
                                                       (tail s))))
        (else (filter pred (tail s)))))

;;;累积函数
;;;将流中的每个元素,按照combiner累积起来
(define (accumulate combiner init-val s)
  (if (empty-stream? s)
      init-val
      (combiner (head s)
                (accumulate combiner init-val (tail s)))))

;;;将树上的叶子结点append起来,成为一个流
(define (enumerate-tree tree)
  (if (leaf-node? tree)
       (cons-stream tree
                    the-empty-stream)
      (append-streams
       (enumerate-tree
        (left-branch tree))
       (enumerate-tree
        (right-branch tree)))))

;;;将两个流合并成一个流
(define (append-streams s1 s2)
  (if (empty-stream? s1)
      s2
      (cons-stream
       (head s1)
       (append-streams (tail s1)
                       s2))))

(define (enum-interval low high)
  (if (> low high)
       the-empty-stream
       (cons-stream
        low
        (enum-interval (+ 1 low) high))))


(define (sum-odd-squares tree)
  (accumulate
   +
   0
   (map square
        (filter odd
                (enumerate-tree tree)))))

(define (odd-fibs n)
  (accumulate
   cons
   '()
   (filter odd
           (map fib
                (enum-interval 1 n)))))


;;;重点:
;;;we're establishing conventional interfaces



;;;============================================================
;;;难的是accumulate
;;;map filter accumulate


;;;flatten压平,返回值为一个流
;;;一个其中的每个元素都是流的流
(define (flatten stream-of-stream)
  (accumulate append-streams the-empty-stream stream-of-stream))
;;;依次对每个子流进行append
;;;((a b c ... ) (1 2 3 ... ) (A B C ... ) ... )变为
;;;(a b c ... 1 2 3 ... A B C ... ... )
;;;第一个子流s1,append第二个子流s2,append第三个子流s3,...
;;;+++++把所有的元素从子流中提取出来,把它们串接在一起+++++


;;其中f入参为一个元素,输出为一个流,也就是一个产生流的procedure
;;返回值也是一个流
(define (flatmap f s)
  (flatten (map f s)))
;;;flatmap就是flatten和map的结合


;;;返回值是一个流
(collect <result> <第一个流的范围,第二个流的范围> <filter>)
;;;============================================================


;;;0<j<i<=n,并且i+j为prime
;;;将1到n中的每个数字,变成一个流
;;;1到n本身就是个流,也就是(flatmap f s)
;;;这样就变成了流的流stream-of-stream,然后传递给flatten
;;;对1到n中的每个i,生成一个流,比如i为3,即(1 2 3),则变成((3 2) (3 1))
(flatmap
 (lambda (i) ;;;对每一个i进行map
   (map (lambda (j) (list i j))
        (enum-interval 1 (-1+ i))))
 (enum-interval 1 n))

;;;(2 1)的car为2,cdr为(1),cadr为1
;;;所以这里为cadr
(filter
 (lambda (p)
   (prime? (+ (car p) (cadr p))))
 (flatmap ... ))

(define (prime-sum-pairs n)
  (map
   (lambda (p)
     (list (car p) (cadr p) (+ (car p) (cadr p))))
   (filter ... )))

;;;总结:
;;;思路:
;;;1.将1到n的每个数字,映射为流(i j),通过(flatmap f s)
;;;2.过滤stream-of-stream中的和为prime的元素
;;;3.将结果在映射为(i j i+j)



;;;通过使用flatmap,我们可以实现其他语言中的嵌套循环
;;;by using flatmaps, we can do what would correspond to
;;;nested loops in most other languages


;;;引进collect,用来代表特定顺序的flatmap和filter操作
;;;collect is for that nest of flatmaps and filters arranged in that
;;;particular way
(define (prime-sum-pairs n)
  (collect
   (list i j (+ i j)) ;;;对应结果
   ((i (enum-interval 1 n))
    (j (enum-interval 1 (-1+ i)))) ;;;对应flatmap
   (prime? (+ i j)))) ;;;对应filter


;;;嵌套的flatmap


;;;八皇后问题
;;;backtracking search
;;;过于关注时间
;;;this program is too inordinately concerned with time
;;;什么是时间?时间就是step

;;;在<rest-of-positions>这些位置已经放置棋子的条件下,
;;;在<row>行<column>列放置棋子是否安全
(safe? <row> <column> <rest-of-positions>)



;;;不把这颗树看做是逐步生成的
;;;而是假设所有的东西都已经生成好了
;;;假设k-1层已经生成好了,现在要生成第k层
;;;递归策略
;;;最终结果是个流


;;;size行的前k列
(define (queens size)
  (define (fill-cols k) ;;;填充第k列
    (if (= k 0)
        (singleton empty-board)
        (collect ... )))
  (fill-cols size))

(collect
 (adjoin-position try-row
                  k
                  rest-queens)
 ((rest-queens (fill-cols (-1+ k))) ;;;k-1列的所有放置方法
  (try-row (enum-interval 1 size)))
 (safe? try-row k rest-queens))

;;;总结:
;;;1.编写子过程,填充第k列,(fill-cols k),是一个递归的,从第8列,到第7列,到。。。
;;;2.收集一些东西,找出k-1列的所有的放置,以及第k列的所有的可放置位置(即:1到8)
;;;3.过滤出为安全的位置
;;;最后得到结果流


;;;组合程序的方法
;;;find the second prime between 10000 and 1000000
(head
 (tail
  (filter prime? (enum-interval 10000 1000000))))

;;其中(enum-interval 10000 1000000)就是:
(cons 10000
      (delay (enum-interval 10001 1000000)))


;;;每个盒子代表了一个集合
;;;interval             filter
;;;10000     -------->  prime? ------->
;;;1000000

;;;stream are not list
;;;流不是表

;;;能够增量式的计算自己,一种按需的数据结构
;;;we want to arrange for a stream to be a data structure
;;;that sorts of computes itself incrementally
;;;sort of on-demand data structure

;;;本质:数据与过程之间,并没有明显界限
;;;流就是一种用过程表示的数据,特别是tail部分
;;;tail部分是一个无参过程(lambda () ... )

(cons-stream x y) ---> (cons x (delay y))
(head s) --> (car s)
(tail s) --> (force (cdr s)) ;;;(cdr s) --> (delay y),而不是((delay y)),因为是pair,而不是list


(define (enum-interval low high)
  (if (> low high)
       the-empty-stream
       (cons-stream
        low
        (enum-interval (+ 1 low) high))))
(head (tail (filter prime? (enum-interval 10000 100000000))))
                           ;;;这部分相当于,因为low不大于high
                           (cons-stream 10000 (enum-interval 10001 100000000))
                           即:(cons 10000 (delay (enum-interval 10001 100000000)))
            (filter prime? (tail (cons 10000 (delay (enum-interval 10001 100000000))))) ;;;force this promise
                           即:(force (delay (enum-interval 10001 100000000)))
                           即:(enum-interval 10001 100000000) 
                           即:(cons-stream 10001 (enum-interval 10002 100000000))
                           即:(cons 10001 (delay (enum-interval 10002 100000000)))
                           ...
                           直到:(cons 10009 (delay (enum-interval 10010 100000000))) 找到第一个质数
                           然后再找第二个


;;;delay ---> 产生一个promise
;;;即:将<exp>的立即求值,转变为lambda,因为lambda不会立即求值
(delay <exp>) ---> (lambda () <exp>)
(force p) ---> (p) ;;;p为promise

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(force p) --> (p)
(force (delay p)) ---> (p)
((delay p))
((lambda () p))
(p)
((lambda () ... )) ---> (...)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;de-couple

;;;如果一个流是通过(cons-stream a (cons-stream b ... )),不停的cons-stream得到的
;;;(tail (tail (tail ... )))
;;;修改delay的定义
(delay <exp>):
(memo-proc
 (lambda ()
   <exp>))


;;接受一个无参过程
(define (memo-proc proc)
  (let ((already-run? nil) (result nil))
    (lambda ()
      (if (not already-run?)
          (sequence
            (set! result (proc))
            (set! already-run? (not nil))
            result)
          result))))

;;;比如,已经知道了(tail stream)了
;;;那么,再次计算(tail (tail stream))时,内部的(tail stream)就不用再次计算了

;;;我们把数据结构组织的像一个过程
;;;we've written data structures that, in fact, are sort of like procedures.


;;;参考资料:
;;; https://github.com/xavier/sicp-notes/blob/master/lecture_06a.md
;;; https://chowdera.com/2021/11/20211129225341901Q.html
;;; https://people.eecs.berkeley.edu/~bh/ssch8/higher.html
;;; https://mitpress.mit.edu/sites/default/files/sicp/code/ch3.scm



;;;0<J<I<=N,取其中和为prime的组合
;;;将1到n的流,映射为 流的流
(flatmap
 (lambda (i)
   (map (lambda (j) (list i j)) (enum-interval 1 (- i 1))))
 (enum-interval 1 n))
;;;过滤出其中的prime
(filter
 (lambda (p)
   (prime? (+ (car p) (cadr p))))
 (flatmap ... ))
;;;将结果映射为(i j (i+j))
(map (lambda (x)
       (list (car x) (cadr x) (+ (car x) (cadr x))))
     (filter ...))

;;;改为collect的方式
(collect (list i j (+ i j))
         ((i (enum-interval 1 n))
          (j (enum-interval 1 i)))
         (prime? (+ i j)))


;;;八皇后问题
(define (queens size)
  ;;;递归填充第k列,k为8,7,6,。。。1,0
  (define (fill-cols k)
    (if (= k 0)
        (singleton empty-board)
        ;;;收集第k列的所有可能位置
        (collect ... )))
  (fill-cols size))

(collect
 ;;;结果
 (adjoin-position row k rest-queens)
 ;;;流
 ;;;1.已经存在的集合或者叫流,也就是填满第k-1列时,已经放置的位置
 ((rest-queens (fill-cols (- k 1)))
  ;;;2.row的可选集合或者叫流
  (row (enum-interval 1 n)))
 ;;;过滤
 (safe? row k rest-queens))


(cdr '(a b)) ;;;'(a b)是(list a b),而不是(cons a b)
(car '(a b))
(cdr '(x (delay y))) ;;;'(x (delay y))是(list x (delay y)),而不是(cons x (delay y))

(cdr (cons 'a 'b)) ;;; ---> b

(cdr (cons 'x (lambda () (+ 1 2))))
((cdr (cons 'x (lambda () (+ 1 2)))))


;;;https://www.thinbug.com/q/942580
;;;https://courses.cs.washington.edu/courses/cse341/04wi/lectures/14-scheme-quote.html
;;;google scheme run quote lambda


;;; pair:
;;;   |--------|-------|
;;;   |    a   |  b    |
;;;   |        |       |
;;;   |--------|-------|
;;;
;;;
;;; list:
;;;  |------|-------|        |------|---|
;;;  |   a  |   ----|------->|  b   | / |
;;;  |      |       |        |      |/  |
;;;  |------|-------|        |------|---|      
;;;
;;;
;;;  pair:
;;;  |------|-------|        |------|-----|
;;;  |   a  |   ----|------->|  b   | c   |
;;;  |      |       |        |      |     |
;;;  |------|-------|        |------|-----|
;;;
;;;


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值