ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
附录D 可设为infinity的时钟 ==================== Guile的过程`alarm`提供了一种可中断的定时器机制。用户可以给这个时钟设置或重置一些时间片,或者让它停止。当时钟的定时器递减到0后,它就会执行用户之前设定的动作。Guile的`alarm`不是一个类似于第十五章第一节里定义的那种时钟,但是我们可以很容易的把它改造成那样。 时钟的定时器的初始状态是停止的,也就是说它不会随着时间流逝而被“触发”。如果想把定时器的触发时间设置为`n`秒(`n`不为0),运行`(alarm n)`。如果定时器已经被设定过了,那么`(alarm n)`就返回该定时器在本次设定前剩余的秒数。如果之前没有设定过,则返回0。 执行`(alarm 0)`让时钟的定时器停止,即定时器中的计数器【你可以理解为一个变量】不会随时间而递减,而且不会触发。`(alarm 0)`同样返回定时器在本次设定前剩余的秒数(如果之前设定过的话)。 默认情况下,当时钟的定时器计数减到0时,Guile会在控制台上显示一条消息并退出。更多的行为可以用过程`sigaction`来设定,如下所示: ```scheme (sigaction SIGALRM (lambda (sig) (display "Signal ") (display sig) (display " raised. Continuing...") (newline))) ``` 第一个参数`SIGALRM`(恰好是14)告诉`sigaction`需要设定的时钟处理函数[1]。第二个参数是一个用户指定的单参数过程。在这个例子里,当时钟触发时,处理函数会在控制台上显示`"Signal 14 raised. Continuing..."`而不是退出Scheme(14是变量`SIGALRM`的值,时钟会把它传递给它对应的处理过程,我们现在先不考虑这个)。 从我们的角度看,这种简单的定时器机制有一个问题。过程`alarm`的返回值`0`的意义是不明确的:既可能是指时钟处于停止状态,也有可能是刚好计时器减到了0。我们可以通过在时钟的算法里引入`*infinity*`来解决这个问题。换句话说,我们需要的时钟与`alarm`基本上是差不多的,除了一点,那就是如果时钟停止的话,那么它有`*infinity*`秒。这样就看起来比较自然了。 1. `(clock n)`对于一个停止的时钟返回`*infinity*`,而不是0。 2. 如果让时钟停止,执行`(clock *infinity*)`,而不是`(clock 0)`。 3. `(clock 0)`相当于给时钟设置一个无限小的时间,也就是让它立即触发。 在Guile中,我们可以把`*infinity*`定义为如下的“数”: ```scheme (define *infinity* (/ 1 0)) ``` 我们用`alarm`来定义`clock`。 ```scheme (define clock (let ((stopped? #t) (clock-interrupt-handler (lambda () (error "Clock interrupt!")))) (let ((generate-clock-interrupt (lambda () (set! stopped? #t) (clock-interrupt-handler)))) (sigaction SIGALRM (lambda (sig) (generate-clock-interrupt))) (lambda (msg val) (case msg ((set-handler) (set! clock-interrupt-handler val)) ((set) (cond ((= val *infinity*) ;This is equivalent to stopping the clock. ;This is almost equivalent to (alarm 0), except ;that if the clock is already stopped, ;return *infinity*. (let ((time-remaining (alarm 0))) (if stopped? *infinity* (begin (set! stopped? #t) time-remaining)))) ((= val 0) ;This is equivalent to setting the alarm to ;go off immediately. This is almost equivalent ;to (alarm 0), except you force the alarm ;handler to run. (let ((time-remaining (alarm 0))) (if stopped? (begin (generate-clock-interrupt) *infinity*) (begin (generate-clock-interrupt) time-remaining)))) (else ;This is equivalent to (alarm n) for n != 0. ;Just remember to return *infinity* if the ;clock was previously quiescent. (let ((time-remaining (alarm val))) (if stopped? (begin (set! stopped? #f) *infinity*) time-remaining)))))))))) ``` 过程`clock`用到了三个内部状态变量: 1. `stopped?`,表示时钟是否是停止的; 2. `clock-interrupt-handler`,一个过程,表示用户希望在时钟触发后执行的动作; 3. `generate-clock-interrupt`,另一个过程,该过程会在运行用户定义的时钟处理过程前把`stopped?`设为`false`。 过程`clock`有两个参数。如果第一个参数是`set-handler`,那么就把第二个参数作为时钟处理器。 如果第一个参数是`set`,就把该时钟触发时间设置为第二个参数,返回本次设定前定时器剩余的秒数。代码对`0`、`*infinity*`以及其他时间值的处理是不同的,这样用户可以得到一个算术上对`alarm`透明的接口。 ---------------------------------------- [1]. 还有一些其他的信号和与之相应的处理器,`sigaction`同样可以使用它们。