在本页中:
4.8.1 作用在前:begin
4.8.2 作用在后:begin0
4.8.3 按条件作用:whenunless

4.8 序列

Racket 程序员更喜欢编写副作用尽可能少的程序,因为纯函数式代码更容易测试 并组合到更大的程序中。然而,与外部环境交互需要序列,如写入到显式, 打开图形窗口或操作磁盘上的文件等。

4.8.1 作用在前:begin

+The Racket ReferenceSequencing: begin, begin0, and begin-for-syntax一节中也提供了begin的文档。

begin 表达式用于串连表达式:

(begin expr ...+)

expr 会按顺序求值,且除最后一个 expr 之外的所有结果都会被忽略。最后一个 expr 的结果即为整个 begin 形式的结果,它在对应的 begin 形式的尾部。

例如:
(define (print-triangle height)
  (if (zero? height)
      (void)
      (begin
        (display (make-string height #\*))
        (newline)
        (print-triangle (sub1 height)))))
> (print-triangle 4)

****

***

**

*

很多形式,例如 lambdacond 即便没有 begin 也支持表达式序列。有时可以说这样的位置拥有一个 隐式 begin

例如:
(define (print-triangle height)
  (cond
    [(positive? height)
     (display (make-string height #\*))
     (newline)
     (print-triangle (sub1 height))]))
> (print-triangle 4)

****

***

**

*

begin 形式在顶级、模块级或只在内部定义中作为 body 时是特殊的。 在这些位置,它并不会构成一个表达式,而是 begin 的内容会被分割为周围的上下文。

例如:
> (let ([curly 0])
    (begin
      (define moe (+ 1 curly))
      (define larry (+ 1 moe)))
    (list larry curly moe))

'(2 0 1)

这种分割的行为主要用于宏,我们会在之后的 Macros 一章中讨论。

4.8.2 作用在后:begin0

+The Racket ReferenceSequencing: begin, begin0, and begin-for-syntax一节中也提供了begin0的文档。

begin0 表达式的语法与 begin 表达式相同:

(begin0 expr ...+)

不同之处在于 begin0 返回第一个 expr 的结果,而非最后一个 expr 的结果。begin0 形式对于实现发生在计算之前的副作用, 特别是产生结果数量未知的计算来说很有用。

例如:
(define (log-times thunk)
  (printf "Start: ~s\n" (current-inexact-milliseconds))
  (begin0
    (thunk)
    (printf "End..: ~s\n" (current-inexact-milliseconds))))
> (log-times (lambda () (sleep 0.1) 0))

Start: 1536501280785.732

End..: 1536501280889.694

0

> (log-times (lambda () (values 1 2)))

Start: 1536501280890.187

End..: 1536501280890.253

1

2

4.8.3 按条件作用:whenunless

+The Racket ReferenceGuarded Evaluation: when and unless一节中也提供了whenunless的文档。

when 形式为 “then” 从句结合了 if 风格的条件和序列而没有 “else” 从句:

(when test-expr then-body ...+)

test-expr 产生了真值,那么所有的 then-body 都会被求值。 最后一个 then-body 即为 when 形式的结果。否则 then-body 均不会被求值,且其结果为 #<void>

unless 形式是类似的:

(unless test-expr then-body ...+)

不同之处在于 test-expr 的结果是相反的:then-body 只在 test-expr 的结果为 #f 时才会被求值。

例如:
(define (enumerate lst)
  (if (null? (cdr lst))
      (printf "~a.\n" (car lst))
      (begin
        (printf "~a, " (car lst))
        (when (null? (cdr (cdr lst)))
          (printf "and "))
        (enumerate (cdr lst)))))
> (enumerate '("Larry" "Curly" "Moe"))

Larry, Curly, and Moe.

(define (print-triangle height)
  (unless (zero? height)
    (display (make-string height #\*))
    (newline)
    (print-triangle (sub1 height))))

 

> (print-triangle 4)

****

***

**

*