为什么球拍将 #<void> 传递给我的函数而不是状态?

Why is racket passing #<void> to my function instead of the state?

我正在学习 Racket,并使用《Realm Of Racket》这本书。我到达了他们向您展示如何为向下移动的图像制作动画的地步 window,然后建议您尝试修改它以响应向左或向右箭头键的按下以横向移动图像。

看起来很简单...

#lang racket

(require 2htdp/universe 2htdp/image)

(define WIDTH 500)
(define DX 0)
(define HEIGHT 500)
(define IMAGE .) ; The image doesn't show up here

(define (add3-to-state current-state)
  (+ current-state 3))

(define (draw-image current-state)
  (place-image IMAGE (+ DX (/ WIDTH 2)) current-state (empty-scene WIDTH HEIGHT)))

(define (state-is-500 current-state)
  (>= current-state 459))

(define (set-delta current-state key)
  (set! DX (+ DX 10)))

(big-bang 0
  (on-tick add3-to-state)
  (on-key set-delta)
  (to-draw draw-image)
  (stop-when state-is-500)
  )

当我运行这个的时候,图像像以前一样向下移动,但是当我按下一个键时,我得到一个错误...

>=: contract violation
   expected: real?
   given: #<void>

...在 state-is-500 函数中。

谁能解释为什么会这样?据我所知,on-key 的函数与书中显示的函数具有相同的形式。

我尝试将 state-is-500 函数修改为如下所示...

(define (state-is-500 current-state)
  (printf "~a\n" current-state)
  (>= current-state 459))

...我得到的输出是...

102
105
#<void>

...在错误发生之前。

所以看起来 #<void> 正在传递给函数,但为什么呢?

谢谢

问题是 big-bang 期望按键上的函数调用 return 游戏的新状态。您没有重新调整任何东西,因此框架报告 <void>

因此,如果状态没有改变,您应该 return 输入状态,例如

(define (set-delta current-state key)
  current-state)

这个框架和 Racket 本身也让你不想改变变量,也就是说你不给它们赋值,或者至少你只给它们赋值一次。因此 set! 不是你应该经常使用的东西。

在这种情况下,本书提供的应用程序状态只是垂直距离,因此只是一个数字,如 add3-to-state 所示。原始状态作为第一个参数传入big-bang。

你要改变的是,现在的状态不仅仅是上下运动,还有左右运动。此移动显示为 place-image 的第二个参数。所以你必须有一个可以提供第二个和第三个参数的状态。