zoukankan      html  css  js  c++  java
  • SICP学习笔记 (2.2.4)

                                                                SICP学习笔记 (2.2.4)
                                                                        周银辉

    1,Scheme的GUI编程

    很幸运的是,PLT scheme提供了GUI库,叫做“MrEd”,在DrScheme中可以直接使用,但需要在IDE的左下角将语言选择为Module,并且在代码开始处加上#lang scheme/gui,具体的语法信息可以参考这里:http://docs.plt-scheme.org/gui/index.html

     下面这段代码,画了一个小头像

    #lang scheme/gui

    ;定义一些画刷
    (define no
    -pen (make-object pen% "BLACK" 1 'transparent))
    (define red-pen (make-object pen% "RED" 2 'solid))
    (define black-pen (make-object pen% "BLACK" 2 'solid))
    (define no-brush (make-object brush% "BLACK" 'transparent))
    (define yellow-brush (make-object brush% "YELLOW" 'solid))
    (define red-brush (make-object brush% "RED" 'solid))

    ;定义图形
    (define (draw
    -face dc)
      (send dc set
    -smoothing 'smoothed)
      (send dc set-pen black-pen)
      (send dc set
    -brush no-brush)
      (send dc draw
    -ellipse 50 50 100 100)
      (send dc set
    -brush yellow-brush)
      (send dc draw
    -line 70 100 90 100)
      (send dc draw
    -ellipse 50 90 20 20)
      (send dc draw
    -ellipse 90 90 20 20)
      (send dc set
    -brush no-brush)
      (send dc set
    -pen red-pen)
      (let ([
    -pi (atan 0 -1)])
        (send dc draw
    -arc 50 60 60 80 (* 3/2 -pi) (* 7/4 -pi))))

    ;定义一个窗口
    (define myWindow (new frame
    % [label "example window"
                       [width 
    300] [height 300]))

    ;定义一个面板,附着在刚才的窗口上
    (define myCanvas (new canvas
    % 
                          [parent myWindow]
                          ;事件处理,Paint回调时将draw
    -face
                          [paint
    -callback (lambda (canvas dc) (draw-face dc))]))

    (send myWindow show 
    #t)

     



    2,向量和向量操作

    我这里用List来定义的向量,其实也可以用cons以及其他任何可行的方式,但都比较简单:

    (define (make-vect x y) (list x y))

    (define (xcor-vect v) (car v))

    (define (ycor-vect v) (cadr v))

    (define (add-vect v1 v2)
      (make-vect (+ (xcor-vect v1) (xcor-vect v2))
                 (+ (ycor-vect v1) (ycor-vect v2))))

    (define (sub-vect v1 v2)
      (make-vect (- (xcor-vect v1) (xcor-vect v2))
                 (- (ycor-vect v1) (ycor-vect v2))))

    (define (scale-vect s v)
      (make-vect (* s (xcor-vect v))
                 (* s (ycor-vect v))))

    (define (length v)
      (sqrt (+ (* (xcor-vect v) (xcor-vect v))  (* (ycor-vect v) (ycor-vect v)))))

    (define (sinθ v)
      (/ (ycor-vect v) (length v)))

    (define (cosθ v)
      (/ (xcor-vect v) (length v)))

    (define (rotation-vect v θ)
      (let ((x (xcor-vect v))
            (y (ycor-vect v)))
        (make-vect (- (* x (cos θ)) (* y (sin θ)))
                   (+ (* x (sin θ)) (* y (cos θ))))))

     其中length是求向量的长度, sinθ和cosθ是求向量与x轴夹角的正弦与余弦值。 rotation-vect将向量绕X轴旋转θ度(弧度)

    3, 定义Frame

    (define (make-frame origin edge1 edge2)
      (list origin edge1 edge2))

    (define (origin-frame f)
      (car f))

    (define (edge1-frame f)
      (cadr f))

    (define (edge2-frame f)
      (caddr f))


    (define (frame-coord-map frame)
      (lambda (v)
        (add-vect
         (origin-frame frame)
         (add-vect (scale-vect (xcor-vect v)
                               (edge1-frame frame))
                   (scale-vect (ycor-vect v)
                               (edge2-frame frame))))))

    我这里只采用的List的方式来定义,练习2.47中要求用list和cons两种方式,cons方式这里就不给出了,依葫芦画瓢即可

    4,定义线段

    (define (make-segment v-start v-end)
      (cons v-start v-end))

    (define (start-segment seg)
      (car seg))

    (define (end-segment seg)
      (cdr seg))


    (define (draw-segment dc seg)
      (let ((v-start (start-segment seg))
            (v-end (end-segment seg)))
        (send dc draw-line
          (xcor-vect v-start)
          (ycor-vect v-start)
          (xcor-vect v-end)
          (ycor-vect v-end))))

    其中draw-segment 方法是关键,其用一个指定的dc来绘制线段,由于MrEd中绘制线段时要求传入的是x1 y1 x2 y2四个数值而非点坐标,所以上稍稍转换了一下

    5,绘制线段列表

    (define (segments->painter dc segment-list)
      (lambda (frame)
        (for-each
          (lambda (segment)
            (let ((new-start-segment ((frame-coord-map frame) (start-segment segment)))
                  (new-end-segment ((frame-coord-map frame) (end-segment segment))))
            (draw-segment
              dc
              (make-segment new-start-segment new-end-segment))))
          segment-list)))

    一个for-each语句就可以搞定了,但需要注意的是这里将frame拉了进来,所以在调用draw-segment时传入的点坐标必须是经过frame映射之后的,也就是我们上面的new-start-segment 和 new-end-segment

    6,一个简单的实例

    经过上面5点的预备知识,我们现在便可以定义一个线段列表来绘制一个由线段组成的图形了,下面是一个简单的示例代码:

    #lang scheme/gui

    ;---------------vector---------------------------
    (define (make-vect x y) (list x y))

    (define (xcor-vect v) (car v))

    (define (ycor-vect v) (cadr v))

    (define (add-vect v1 v2)
      (make-vect (+ (xcor-vect v1) (xcor-vect v2))
                 (+ (ycor-vect v1) (ycor-vect v2))))

    (define (sub-vect v1 v2)
      (make-vect (- (xcor-vect v1) (xcor-vect v2))
                 (- (ycor-vect v1) (ycor-vect v2))))

    (define (scale-vect s v)
      (make-vect (* s (xcor-vect v))
                 (* s (ycor-vect v))))

    (define (length v)
      (sqrt (+ (* (xcor-vect v) (xcor-vect v))  (* (ycor-vect v) (ycor-vect v)))))

    (define (sinθ v)
      (/ (ycor-vect v) (length v)))

    (define (cosθ v)
      (/ (xcor-vect v) (length v)))

    (define (rotation-vect v θ)
      (let ((x (xcor-vect v))
            (y (ycor-vect v)))
        (make-vect (- (* x (cos θ)) (* y (sin θ)))
                   (+ (* x (sin θ)) (* y (cos θ))))))

    ;---------------Frame---------------------------
    (define (make-frame origin edge1 edge2)
      (list origin edge1 edge2))

    (define (origin-frame f)
      (car f))

    (define (edge1-frame f)
      (cadr f))

    (define (edge2-frame f)
      (caddr f))


    (define (frame-coord-map frame)
      (lambda (v)
        (add-vect
         (origin-frame frame)
         (add-vect (scale-vect (xcor-vect v)
                               (edge1-frame frame))
                   (scale-vect (ycor-vect v)
                               (edge2-frame frame))))))


    ;---------------segment---------------------------

    (define (make-segment v-start v-end)
      (cons v-start v-end))

    (define (start-segment seg)
      (car seg))

    (define (end-segment seg)
      (cdr seg))


    (define (draw-segment dc seg)
      (let ((v-start (start-segment seg))
            (v-end (end-segment seg)))
        (send dc draw-line
          (xcor-vect v-start)
          (ycor-vect v-start)
          (xcor-vect v-end)
          (ycor-vect v-end))))


    (define (segments->painter dc segment-list)
      (lambda (frame)
        (for-each
          (lambda (segment)
            (let ((new-start-segment ((frame-coord-map frame) (start-segment segment)))
                  (new-end-segment ((frame-coord-map frame) (end-segment segment))))
            (draw-segment
              dc
              (make-segment new-start-segment new-end-segment))))
          segment-list)))

    ;---------------------------------------------------------

    (define red-pen (instantiate pen% ("RED" 2 'solid)))

    ;一个线段列表  -_-!
    (define mySegmentList
      (list
        (make-segment
          (make-vect 0.1 0.4)
          (make-vect 0.3 0.4))
        (make-segment
          (make-vect 0.5 0.4)
          (make-vect 0.7 0.4))
        (make-segment
          (make-vect 0.3 0.6)
          (make-vect 0.5 0.6))
        (make-segment
          (make-vect 0.8 0.3)
          (make-vect 0.8 0.55))
        (make-segment
          (make-vect 0.78 0.6)
          (make-vect 0.80 0.6))
        (make-segment
          (make-vect 0.9 0.3)
          (make-vect 0.9 0.55))
        (make-segment
          (make-vect 0.88 0.6)
          (make-vect 0.90 0.6))))

    ;定义我们的Frame
    (define myFrame
      (make-frame
        (make-vect 0 0)
        (make-vect 200 0)
        (make-vect 0 200)))

    ;定义一个窗口
    (define myWindow (new frame% [label "example window"]
                       [width 300] [height 300]))

    ;定义一个面板,附着在刚才的窗口上
    (define myCanvas (new canvas%
                          [parent myWindow]
                          ;事件回调    
                          [paint-callback (lambda (canvas dc)
                                            (begin
                                              (send dc set-pen red-pen)
                                              ( (segments->painter dc mySegmentList) myFrame)))]))

    (send myWindow show #t)


    运行效果如下:

     

    7,beside 和 below

    其实在SICP本节的最后是给了beside方法的(below被留成了练习2.51),但它们都是基于transform-painter方法的,在学会transform-painter 方法之前,我们还是有办法做到了,运用一点三角函数的知识就可以了(准备一张草稿纸,画画直角坐标系和三角函数):

    (define (beside painter1 painter2)
      (lambda (frame)
        (let ((f1 (make-frame
                   (origin-frame frame)
                   (make-vect
                    (* (/ (length (edge1-frame frame)) 2.0) (cosθ (edge1-frame frame)))
                    (* (/ (length (edge1-frame frame)) 2.0) (sinθ (edge1-frame frame))))
                   (edge2-frame frame )))
              (f2 (make-frame
                   (make-vect
                    (* (/ (length (edge1-frame frame)) 2.0) (cosθ (edge1-frame frame)))
                    (* (/ (length (edge1-frame frame)) 2.0) (sinθ (edge1-frame frame))))
                   (make-vect (/ (xcor-vect(edge1-frame frame)) 2.0) (/ (ycor-vect(edge1-frame frame)) 2.0))
                   (edge2-frame frame ))))
          (painter1 f1)
          (painter2 f2))))


    (define (below painter1 painter2)
      (lambda (frame)
        (let ((f1 (make-frame
                   (origin-frame frame)              
                   (edge1-frame frame )
                   (make-vect
                    (* (/ (length (edge2-frame frame)) 2.0) (cosθ (edge2-frame frame)))
                    (* (/ (length (edge2-frame frame)) 2.0) (sinθ (edge2-frame frame))))))
              (f2 (make-frame
                   (make-vect
                    (* (/ (length (edge2-frame frame)) 2.0) (cosθ (edge2-frame frame)))
                    (* (/ (length (edge2-frame frame)) 2.0) (sinθ (edge2-frame frame))))
                   (edge1-frame frame )
                   (make-vect (/ (xcor-vect(edge2-frame frame)) 2.0) (/ (ycor-vect(edge2-frame frame)) 2.0)))))
          (painter1 f1)
          (painter2 f2))))


     上面的代码有不少语句是重复的,你可以用let变量重构一下,然后看看我们的below效果:

     

    8,练习2.45

    (define (split combine-main combine-smaller)
      (lambda (painter n)
        (if (zero? n)
          painter
          (let ((smaller ((split combine-main combine-smaller) painter (- n 1))))
            (combine-main
              painter
              (combine-smaller smaller smaller))))))

    9,练习2.46,2.47,2.48,2.49

    2.46、2.47、2.48 前面已经给出答案了哈,copy 一下吧。2.49的直接略掉

    10,练习2.50

    (define (rotate90 painter)
      (transform-painter
        painter
        (make-vect 0.0 1.0)     ; new origin
        (make-vect 0.0 0.0)     ; new end of edge1
        (make-vect 1.0 1.0)))   ; new end of edge2

    (define (rotate180 painter)
      (transform-painter
        painter
        (make-vect 1.0 1.0)
        (make-vect 0.0 1.0)
        (make-vect 1.0 0.0)))

    (define (rotate270 painter)
      (transform-painter
        painter
        (make-vect 1.0 0.0)
        (make-vect 1.0 1.0)
        (make-vect 0.0 0.0)))

    (define (flip-horiz painter)
      (transform-painter
        painter
        (make-vect 1.0 0.0)
        (make-vect 0.0 0.0)
        (make-vect 1.0 1.0)))

    11,练习2.51

    (define (below painter1 painter2)
      (let ( (split-point (make-vect 0.0 0.5))
              (paint-up
                (transform-painter
                  painter2
                  (make-vect 0.0 0.0)
                  (make-vect 1.0 0.0)
                  split-point))
              (paint-down
                (transform-painter
                  painter1
                  split-point
                  (make-vect 1.0 0.5)
                  (make-vect 0.0 1.0))))
        (lambda (frame)
          (paint-up frame)
          (paint-down frame))))

    12,练习2.52

    (define (corner-split painter n)
      (if (zero? n)
        painter
        (let ( (up (up-split painter (- n 1)))
                (right (right-split painter (- n 1)))
                (top-left up)
                (bottom-right right)
                (corner (corner-split painter (- n 1))))
          (beside (below painter top-left)
                  (below bottom-right corner)))))

    13,Functional Geometry

    本节中所有的这些图形变换统称为“Functional Geometry ”,有专门的站点介绍这个: http://www.frank-buss.de/lisp/functional.html 
    完整的代码在这里:

    Functional Geometry (Common Lisp)

     

    注:这是一篇读书笔记,所以其中的内容仅 属个人理解而不代表SICP的观点,并随着理解的深入其中 的内容可能会被修改

  • 相关阅读:
    vs 加入插件
    vs用法
    axios和vue用$refs属性获取dom
    错误演示
    vue表单的用法

    工作
    工作日报
    主机与虚拟机链接
    login
  • 原文地址:https://www.cnblogs.com/zhouyinhui/p/1610854.html
Copyright © 2011-2022 走看看