在本页中:
2.9.1 世界还不够
2.9.2 世界程序的移植
2.9.3 图像程序的移植

2.9 移植世界程序到宇宙

Matthias Felleisen,
Robby Findler,
以及Racket-zh项目组译

2.9.1 世界还不够

随着2009年6月的发布,我们开始弃用world教学包;取代其功能,我们推荐universe教学包。 随着2010年1月的发布,我们还推出了新的image教学包,为了支持这第二个教学包,我们将图像功能与世界程序的功能区分开。

本文档解释如何将旧的世界教学包程序移植到此新设置中,一步一步进行。 最重要的是,程序现在必须import两个教学包而不再是一个:
表的左侧显示的旧的形式,右侧显示新形式。如果你的程序通过drscheme中教学包菜单导入教学包, 我们建议从现在开始使用require形式;或者,可以使用drscheme菜单两次,导入两个教学包。

在下一节中,我们先解释如何移植世界程序,以便他们使用universe教学包和旧的image教学包。 在之后一节中,我们给出更改程序的建议,以便它们不再依赖旧的图像函数,而是使用新图像函数。

为了区分不同的函数,我们在旧函数前统一添加前缀“htdp:”,在新函数前添加“2htdp:”。 当然,你的程序中不需要使用这些前缀。

2.9.2 世界程序的移植

这是world教学包文档中的第一个程序:
(require htdp/world)
 
; Number -> Scene
(define (create-UFO-scene height)
  (htdp:place-image UFO
                    50 height
                    (htdp:empty-scene 100 100)))
 
; Scene
(define UFO
  (htdp:overlay
    (htdp:circle 10 'solid 'red)
    (htdp:rectangle 40 4 'solid 'red)))
 
; ——运行程序
(htdp:big-bang 100 100 (/1 28) 0)
(htdp:on-tick-event add1)
(htdp:on-redraw create-UFO-scene)
这个程序定义了函数将UFO置于100乘100场景中,其中UFO被定义为图像。 世界程序本身由三行组成:
  • 第一行创建100乘100的场景,指定每秒28幅图像的速率,以及0为初始世界描述;

  • 第二行说,对于每个时钟滴答,世界(数值)加1;

  • 最后一行告诉drscheme,使用create-UFO-scene函数将当前世界呈现为场景。

现在让我们一步一步地将此程序转换为使用universe的,从前面说过的require规范开始:

将世界呈现为场景的函数保持不变:

世界形式

宇宙形式

; Number -> Scene
(define (create-UFO-scene height)
  (htdp:place-image
    UFO
    50 height
    (htdp:empty-scene 100 100)))
; Number -> Scene
(define (create-UFO-scene height)
  (htdp:place-image
    UFO
    50 height
    (htdp:empty-scene 100 100)))

图像常量从符号变为字符串:

世界形式

宇宙形式

; Scene
(define UFO
  (htdp:overlay
    (htdp:circle
     10 'solid 'red)
    (htdp:rectangle
     40 4 'solid 'red)))
; Scene
(define UFO
  (htdp:overlay
    (htdp:circle
     10 "solid" "red")
    (htdp:rectangle
     40 4 "solid" "red")))
严格说来,这不是必需的,但我们打算尽可能将符号替换为字符串,因为字符串比符号更常见。

最重要的变化是启动世界程序的几行:

世界形式

宇宙形式

(htdp:big-bang 100 100 (/1 28) 0)
(htdp:on-tick-event add1)
(htdp:on-redraw create-UFO-scene)
(2htdp:big-bang
  0
  (on-tick add1)
  (on-draw create-UFO-scene))
它们被转换为单个表达式,其中包含与旧程序中的行数一样多的子句。正如你所看到的, universe教学包中的big-bang表达不再需要指定场景大小或时钟滴答的速率(当然如果对默认值不满意也可以提供时钟速率)。 此外,这些子句的名称与旧名称相似,但更短。

另一个重大变化涉及键盘和鼠标事件的处理。各个处理程序不再接受符号和字符,而只接受字符串。 world教学包的文档中的第一个键盘事件处理程序是:

世界形式

宇宙形式

(define (change w a-key-event)
  (cond
    [(key=? a-key-event 'left)
     (world-go w -DELTA)]
    [(key=? a-key-event 'right)
     (world-go w +DELTA)]
    [(char? a-key-event)
     w]
    [(key=? a-key-event 'up)
     (world-go w -DELTA)]
    [(key=? a-key-event 'down)
     (world-go w +DELTA)]
    [else
     w]))
(define (change w a-key-event)
  (cond
    [(key=? a-key-event "left")
     (world-go w -DELTA)]
    [(key=? a-key-event "right")
     (world-go w +DELTA)]
    [(= (string-length a-key-event) 1)
     w]
    [(key=? a-key-event "up")
     (world-go w -DELTA)]
    [(key=? a-key-event "down")
     (world-go w +DELTA)]
    [else
     w]))
注意char?子句的改变。由于现在所有字符都表示为包含一个“字母”的字符串, 因此右侧的程序只需检查字符串的长度。除此之外,我们简单地将所有符号更改为字符串。

如果你用过动画gif录制程序的运行,现在仍可以这样做。 但做法不再是向big-bang添加第五个参数,而是添加形如(record? x)的子句。

最后,universe教学包实现了比world教学包更丰富的功能。

2.9.3 图像程序的移植

universe库还带来了新的图像库,2htdp/image。旧的图像库仍然可以协同2htdp/universe正常使用, 但新图像库提供了许多改进,包括更快的图像比较(特别适用于check-expect表达式)、图像旋转、图像缩放、曲线、新的多边形, 以及对线条绘制的更多控制。

要单独使用新图像库:

世界形式

宇宙形式

(require htdp/image)

(require 2htdp/image)

和universe教学包一起使用新图像库:

世界形式

宇宙形式

(require htdp/world)

(require 2htdp/universe)
(require 2htdp/image)

Overlay与Underlay

htdp:overlay函数将其第一个参数放在第二个(及后续)参数之下, 所以在2htdp/image中我们决定将其称为2htdp:underlay

世界形式

宇宙形式

(htdp:overlay
 (htdp:rectangle
  10 20 "solid" "red")
 (htdp:rectangle
  20 10 "solid" "blue"))
(2htdp:underlay
 (2htdp:rectangle
  10 20 "solid" "red")
 (2htdp:rectangle
  20 10 "solid" "blue"))

没有pinhole了

htdp/image中pinhole概念在2htdp/image 中没有对应物 (我们确实希望最终在2htdp/image中加入pinhole,但它们不会像在htdp/image中那样普遍)。

2htdp/image包含了一系列overlay操作, 它们不是对图像中的特殊位置叠加,而是根据其中心或边缘叠加图像。

由于pinhole的默认位置在大多数图像的中心,同时2htdp/image中overlay和underlay图像默认值基于大部分图像的中心, 因此简单的例子(如前所述)在两个库中表现相同。

但是,考虑这个表达式,它按左上角overlay两个图像,分别使用两个库编写。

世界形式

宇宙形式

(htdp:overlay
 (htdp:put-pinhole
  (htdp:rectangle 10 20 "solid" "red")
  0 0)
 (htdp:put-pinhole
  (htdp:rectangle 20 10 "solid" "blue")
  0 0))
(2htdp:underlay/align
 "left"
 "top"
 (2htdp:rectangle
  10 20 "solid" "red")
 (2htdp:rectangle
  20 10 "solid" "blue"))

2htdp/image的版本中,程序使用2htdp:underlay/align指定对齐图像的位置,而不是使用pinhole。

边框位置的不同

htdp/image相比,2htdp/image中边框的图形移动了一个像素。 这意味着下面两个矩形绘制出相同的像素集合。

世界形式

宇宙形式

(htdp:rectangle
 11 11 "outline" "black")
(2htdp:rectangle
 10 10 "outline" "black")

参见像素、画笔和线的细节

Star变了

2htdp:star函数和htdp:star完全不同。两者都是以多边形为基础的星形, 但2htdp:star总是五角星。另请参见2htdp:star-polygon获得更一般的星形。