1 / 67

Programming in Scheme

Programming in Scheme. Continued. 提要. 以数据为中心的软件构造 Scheme 的命令式程序设计设施 小结. 复杂性控制问题. 当面临的问题越来越复杂,我们的程序也会随之越来越复杂,如何控制这种复杂性是软件工程技术的一个中心问题。 应对复杂性: Decomposition ( separation of concerns) Modularity, coupling, coherence Additive development Abstraction Hierarchy. 如何组织程序成为一个关键的问题 易于分工,

val
Télécharger la présentation

Programming in Scheme

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Programming in Scheme Continued

  2. 提要 • 以数据为中心的软件构造 • Scheme的命令式程序设计设施 • 小结

  3. 复杂性控制问题 • 当面临的问题越来越复杂,我们的程序也会随之越来越复杂,如何控制这种复杂性是软件工程技术的一个中心问题。 • 应对复杂性: • Decomposition ( separation of concerns) • Modularity, coupling, coherence • Additive development • Abstraction • Hierarchy

  4. 如何组织程序成为一个关键的问题 • 易于分工, • 易于集成 • 易于局部修改 • 易于构造,易于理解,(writability readability)

  5. 考虑 复数 用数据抽象的办法,可有两种不同内部表示

  6. 必须满足复数的“本质要求”

  7. 对外部而言, • Constructor • make-from-real-imag • make-from-mag-ang • Query (Selector) • real-part • imag-part • magnitude • Angle • (试问,它们要满足那些性质?)

  8. 复数运算 (独立于具体表示) (define(add-complexz1z2)(make-from-real-imag(+(real-partz1)(real-partz2))(+(imag-partz1)(imag-partz2)))) (define(sub-complexz1z2)(make-from-real-imag(-(real-partz1)(real-partz2))(-(imag-partz1)(imag-partz2)))) (define(mul-complexz1z2)(make-from-mag-ang(*(magnitudez1)(magnitudez2))(+(anglez1)(anglez2)))) (define(div-complexz1z2)(make-from-mag-ang(/(magnitudez1)(magnitudez2))(-(anglez1)(anglez2))))

  9. 具体表示方案一 (define (real-part z) (car z))(define (imag-part z) (cdr z)) (define (magnitude z)  (sqrt (+ (square (real-part z))  (square (imag-part z)))))(define (angle z)  (atan (imag-part z) (real-part z))) (define (make-from-real-imag x y) (cons x y))(define (make-from-mag-ang r a)   (cons (* r (cos a)) (* r (sin a))))

  10. 具体表示方案二 (define (real-part z)   (* (magnitude z) (cos (angle z))))(define (imag-part z)   (* (magnitude z) (sin (angle z)))) (define (magnitude z) (car z))(define (angle z) (cdr z)) (define (make-from-real-imag x y)   (cons (sqrt (+ (square x) (square y)))         (atan y x)))(define (make-from-mag-ang r a) (cons r a)))

  11. 数据抽象使得我们的复数运算可以独立于具体表示数据抽象使得我们的复数运算可以独立于具体表示 • 这里我们在复数表示(提供功能的模块)与复数运算(使用功能的模块)之间做了“最小”的约定,从而使得模块提供者一些具体的实现信息对模块使用者隐蔽起来。 • Separation of concerns • 易于独立修改 • 易理解

  12. 给数据贴标签 • 如果我们在一个系统里同时使用上述两个方案,则会引起混淆 • 如何解释(3 . 5)? • 我们可以给这个pair 贴标签

  13. (define (attach-tag type-tag contents)  (cons type-tag contents))(define (attach-tag type-tag contents)  (cons type-tag contents)) (define (type-tag datum)  (if (pair? datum)      (car datum)      (error "Bad tagged datum -- TYPE-TAG" datum))) (define (contents datum)  (if (pair? datum)      (cdr datum)      (error "Bad tagged datum -- CONTENTS"datum)))

  14. (define (rectangular? z)  (eq? (type-tag z) 'rectangular))(define (polar? z)  (eq? (type-tag z) 'polar))(define (rectangular? z)  (eq? (type-tag z) 'rectangular))(define (polar? z)  (eq? (type-tag z) 'polar))

  15. 方案一变为 (define (real-part-rectangular z) (car z))(define (imag-part-rectangular z) (cdr z))(define (magnitude-rectangular z)  (sqrt (+ (square (real-part-rectangular z))           (square (imag-part-rectangular z)))))(define (angle-rectangular z)  (atan (imag-part-rectangular z)        (real-part-rectangular z)))(define (make-from-real-imag-rectangular x y)  (attach-tag 'rectangular (cons x y)))(define (make-from-mag-ang-rectangular r a)   (attach-tag 'rectangular              (cons (* r (cos a)) (* r (sin a)))))

  16. 方案二变为 (define (real-part-polar z)  (* (magnitude-polar z) (cos (angle-polar z))))(define (imag-part-polar z)  (* (magnitude-polar z) (sin (angle-polar z))))(define (magnitude-polar z) (car z))(define (angle-polar z) (cdr z))(define (make-from-real-imag-polar x y)   (attach-tag 'polar           (cons (sqrt (+ (square x) (square y)))                 (atan y x))))(define (make-from-mag-ang-polar r a)  (attach-tag 'polar (cons r a)))

  17. 于是我们有Generic的查询函数 (define (real-part z)  (cond ((rectangular? z)          (real-part-rectangular (contents z)))        ((polar? z)         (real-part-polar (contents z)))        (else (error "Unknown type -- REAL-PART" z))))(define (imag-part z) …) (define (magnitude z)  (cond ((rectangular? z)         (magnitude-rectangular (contents z)))        ((polar? z)         (magnitude-polar (contents z)))        (else (error "Unknown type -- MAGNITUDE" z))))(define (angle z) …) Dispatching on type!

  18. 于是我们的复数运算可利用这些通用查询实现 (define (add-complex z1 z2)  (make-from-real-imag (+ (real-part z1) (real-part z2))                       (+ (imag-part z1) (imag-part z2)))) 可自由地决定如何构造一个复数 (define (make-from-real-imag x y)  (make-from-real-imag-rectangular x y))(define (make-from-mag-ang r a)  (make-from-mag-ang-polar r a))

  19. 新系统结构—进一步解耦

  20. 但仍有问题 • 如果我们需要增加第三种表示,会怎样 • Generic的 real-part imag-part mag angle • 需要将其cond子句通用化

  21. Data-directed Programming • 可设想有一张“表格”, • 每个实现模块填表;而使用时查表。

  22. 假设我们有 • (put <op> <type> <item>) • (get <op> <type>) 于是,方案一和方案二可分别“注册”自己的相关操作实现

  23. (define (install-rectangular-package);; internal procedures  (define (real-part z) (car z))  (define (imag-part z) (cdr z))  (define (make-from-real-imag x y) (cons x y))  (define (magnitude z)    (sqrt (+ (square (real-part z))             (square (imag-part z)))))  (define (angle z)    (atan (imag-part z) (real-part z)))  (define (make-from-mag-ang r a)     (cons (* r (cos a)) (* r (sin a))));; interface to the rest of the system  (define (tag x) (attach-tag ‘rectangular x))  (put ’real-part ‘(rectangular) real-part)  (put ’imag-part ‘(rectangular) imag-part)  (put ’magnitude ‘(rectangular) magnitude)  (put ’angle ‘(rectangular) angle)  (put ’make-from-real-imag ‘rectangular        (lambda (x y) (tag (make-from-real-imag x y))))  (put ’make-from-mag-ang ‘rectangular        (lambda (r a) (tag (make-from-mag-ang r a))))  ’done)(define (install-rectangular-package);; internal procedures  (define (real-part z) (car z))  (define (imag-part z) (cdr z))  (define (make-from-real-imag x y) (cons x y))  (define (magnitude z)    (sqrt (+ (square (real-part z))             (square (imag-part z)))))  (define (angle z)    (atan (imag-part z) (real-part z)))  (define (make-from-mag-ang r a)     (cons (* r (cos a)) (* r (sin a))));; interface to the rest of the system  (define (tag x) (attach-tag ‘rectangular x))  (put ’real-part ‘(rectangular) real-part)  (put ’imag-part ‘(rectangular) imag-part)  (put ’magnitude ‘(rectangular) magnitude)  (put ’angle ‘(rectangular) angle)  (put ’make-from-real-imag ‘rectangular        (lambda (x y) (tag (make-from-real-imag x y))))  (put ’make-from-mag-ang ‘rectangular        (lambda (r a) (tag (make-from-mag-ang r a))))  ’done)

  24. (define (install-polar-package)  ;; internal procedures  (define (magnitude z) (car z))  (define (angle z) (cdr z))  (define (make-from-mag-ang r a) (cons r a))  (define (real-part z)    (* (magnitude z) (cos (angle z))))  (define (imag-part z)    (* (magnitude z) (sin (angle z))))  (define (make-from-real-imag x y)     (cons (sqrt (+ (square x) (square y)))          (atan y x)))  ;; interface to the rest of the system  (define (tag x) (attach-tag 'polar x))  (put 'real-part '(polar) real-part)  (put 'imag-part '(polar) imag-part)  (put 'magnitude '(polar) magnitude)  (put 'angle '(polar) angle)  (put 'make-from-real-imag 'polar       (lambda (x y) (tag (make-from-real-imag x y))))  (put 'make-from-mag-ang 'polar        (lambda (r a) (tag (make-from-mag-ang r a))))  'done)(define (install-polar-package)  ;; internal procedures  (define (magnitude z) (car z))  (define (angle z) (cdr z))  (define (make-from-mag-ang r a) (cons r a))  (define (real-part z)    (* (magnitude z) (cos (angle z))))  (define (imag-part z)    (* (magnitude z) (sin (angle z))))  (define (make-from-real-imag x y)     (cons (sqrt (+ (square x) (square y)))          (atan y x)))  ;; interface to the rest of the system  (define (tag x) (attach-tag 'polar x))  (put 'real-part '(polar) real-part)  (put 'imag-part '(polar) imag-part)  (put 'magnitude '(polar) magnitude)  (put 'angle '(polar) angle)  (put 'make-from-real-imag 'polar       (lambda (x y) (tag (make-from-real-imag x y))))  (put 'make-from-mag-ang 'polar        (lambda (r a) (tag (make-from-mag-ang r a))))  'done)

  25. (define (apply-generic op . args)  (let ((type-tags (map type-tag args)))    (let ((proc (get op type-tags)))      (if proc          (apply proc (map contents args))          (error            "No method for these types -- APPLY-GENERIC"            (list op type-tags))))))(define (apply-generic op . args)  (let ((type-tags (map type-tag args)))    (let ((proc (get op type-tags)))      (if proc          (apply proc (map contents args))          (error            "No method for these types -- APPLY-GENERIC"            (list op type-tags)))))) (define (real-part z) (apply-generic 'real-part z))(define (imag-part z) (apply-generic 'imag-part z))(define (magnitude z) (apply-generic 'magnitude z))(define (angle z) (apply-generic 'angle z))) (define (make-from-real-imag x y)  ((get 'make-from-real-imag 'rectangular) x y))(define (make-from-mag-ang r a)  ((get 'make-from-mag-ang 'polar) r a))

  26. 这种技术让你联想到什么? • Macros? • 不行,运行时刻才能得知具体类型 • 面向对象中的动态绑定 • 但有些不同,这里没有类层次 • (Java Interface?) • Generics • C++ template • Traits技术解决名字问题

  27. 进一步,我们还可以做泛型操作

  28. How? • 与前面类似的策略,加Tag,依据tag选操作 • 不同类型(e.g. 实数与复数)之间的运算? • 转换为同一类型 • (Hierarchy of types)

  29. Imperative Programming in Scheme • 命令式程序设计设施 • Assignments • Local States • Discussions • 支持命令式程序设计的“环境”模型 • Environments and Frames • Evaluation Rules • Procedure Application

  30. 赋值 • set! (define myvar 5) ; myvar  5 (set! myvar 4) ; myvar  4 (set! myvar '(1 2 3)) ; myvar  (1 2 3) • C/Java等语言中常用赋值语言来为变量赋初值, 但Scheme并不鼓励这样做

  31. 例:求二次方程的根 (define quadratic-formula (lambda (a b c) (let ((root1 0) (root2 0) (minusb 0) (radical 0) (divisor 0)) (set! minusb (- 0 b)) (set! radical (sqrt (- (* b b) (* 4 (* a c))))) (set! divisor (* 2 a)) (set! root1 (/ (+ minusb radical) divisor)) (set! root2 (/ (- minusb radical) divisor)) (cons root1 root2))))

  32. 例:更“Scheme”的表达 (define quadratic-formula (lambda (a b c) (let ((minusb (- 0 b)) (radical (sqrt (- (* b b) (* 4 (* a c))))) (divisor (* 2 a))) (let ((root1 (/ (+ minusb radical) divisor)) (root2 (/ (- minusb radical) divisor))) (cons root1 root2)))))

  33. 赋值 • 赋值用以“修改状态” 。 • 除了set!, 常用的还有 set-car! set-cdr! • (define p (list 1 2 3)) • (set-car! (cdr p) 'two) • p ⇒ (1 two 3) • (set-cdr! p '()) • p ⇒ (1)

  34. 例子 • 回想List的定义。如何判断一个对象是否为List? (define (mylist? ls) (if (null? ls) #t (if (pair? ls) (mylist? (cdr ls)) #f))) • 使用set-cdr! 我们可能造成有环的结构 (define ls (list 1 2 3)) (set-cdr! (cdr (cdr ls)) ls) (mylist? ls) 会如何?如何解决

  35. 例: 考虑到环的存在, (define (race hare tortoise) (if (pair? hare) (let ((hare (cdr hare))) (if (pair? hare) (and (not (eq? hare tortoise)) (race (cdr hare) (cdr tortoise))) (null? hare))) (null? hare))) (define (mylist2? x) (race x x))

  36. 局部状态 • 应尽量避免修改全局可见变量;赋值主要用于表达局部可修改状态。 • 有“状态”的对象是我们看待事物的一种常用视角。 • 例如,一个银行账户,原有100元 • (withdraw 25)  75(withdraw 25)  50(withdraw 60)  "Insufficient funds"(withdraw 15)  35 • 账户还是这个账户,余额却在变化

  37. 例:银行账户 (define balance 100) (define (withdraw amount) (if (>= balance amount)     (begin (set! balance (- balance amount))             balance)     "Insufficient funds")) • 注:(begin <exp1> <exp2> ... <expk>) 之含义

  38. 例:银行账户 • 但将balance作为全局可见的变量并未忠实刻画我们对银行账户的认识。应将其作为“封装”起来的状态,外界不能直接访问。 • 比如: (define new-withdraw  (let ((balance 100))    (lambda (amount)      (if (>= balance amount)          (begin (set! balance (- balance amount))                 balance)          "Insufficient funds"))))

  39. 例:银行账户 • 以上只能处理一个账户,如何支持多个账户? (define (make-withdraw balance) (lambda (amount)    (if (>= balance amount)        (begin (set! balance (- balance amount))               balance)        "Insufficient funds"))) (define W1 (make-withdraw 100)) (define W2 (make-withdraw 100)) (W1 50) 50; (W2 70) 30; (W2 40) "Insufficient funds"(W1 40) 10

  40. 更进一步 (define (make-account balance)  (define (withdraw amount)    (if (>= balance amount)        (begin (set! balance (- balance amount))               balance)        "Insufficient funds"))  (define (deposit amount)    (set! balance (+ balance amount))    balance)  (define (dispatch m)    (cond ((eq? m 'withdraw) withdraw)          ((eq? m 'deposit) deposit)          (else (error "Unknown request -- MAKE-ACCOUNT"                       m))))  dispatch)

  41. (define acc (make-account 100)) ((acc 'withdraw) 50)  50 ((acc 'withdraw) 60)  "Insufficient funds" ((acc 'deposit) 40)  90 ((acc 'withdraw) 60)  30 (define acc2 (make-account 100)) ((acc2 'withdraw) 60)  40

  42. 讨论 • 引入赋值和可修改状态的概念,可使我们自然地表达一些常用思维方式;使用恰当,亦有助于软件的模块化。这有助于降低复杂性。 • 代价是:破坏了引用透明性,意味着我们不能再用表达式的值来替换表达式。这在某种程度上又增加了程序的复杂性。 • equals can not be substituted for equals

  43. 讨论 • 考虑: (define peter-acc (make-account 100)) (define paul-acc  (make-account 100)) 以及 (define peter-acc (make-account 100)) (define paul-acc peter-acc) 若对peter-acc进行操作,paul-acc怎样?

  44. 讨论 peter-acc paul-acc 二者同耶?异耶? 何为“同”? 何为“异”? 当我们不使用“可修改的数据”对象时,一个“整体”对象就是其所有组成部分之总和; 意味着,若组成相同,则对象相同; 当我们使用了“可修改的数据”对象时,一个“整体”对象它就拥有了“identity”, 它不只是其组成部分之总和; 意味着,即使组成相同,对象亦不同;

  45. (define (factorial n)  (define (iter product counter)    (if (> counter n)        product        (iter (* counter product)              (+ counter 1))))  (iter 1 1))(define (factorial n)  (define (iter product counter)    (if (> counter n)        product        (iter (* counter product)              (+ counter 1))))  (iter 1 1)) (define (factorial n)  (let ((product 1)        (counter 1))    (define (iter)      (if (> counter n)          product          (begin (set! product (* counter product))                 (set! counter (+ counter 1))                 (iter))))    (iter)))

  46. “环境模型” • 让我们来看看变量实际上放在那里 • 表达式的求值,依赖于其“环境” (environment) • 一个 environment由一系列frames组成 • 除了global frame 外,每个 frame 都有一个 enclosing environment • 一个 frame包含一些列 bindings • name与value

  47. 例如,

  48. 求值规则 • 原先,我们的表达式求值规则是 • 计算每个子表达式的值; • 以操作子表达式的值为操作,作用于各参数子表达式的值; • 现在,我们需要进一步细化这个规则 • 首先看 什么是一个 操作(procedure) • (define (square x) (* x x)) 是 (define square (lambda (x) (* x x))) 的 简写;

  49. define 实际上是 在当前 frame 里面加一个binding procedure由两部分构成: 一部分是参数和体 另一部分是环境(指针)

  50. Evaluate (square 5)

More Related