4.8 序列
Racket 程序员更喜欢编写副作用尽可能少的程序,因为纯函数式代码更容易测试 并组合到更大的程序中。然而,与外部环境交互需要序列,如写入到显式, 打开图形窗口或操作磁盘上的文件等。
4.8.1 作用在前:begin
The Racket Reference的Sequencing: 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)
****
***
**
*
很多形式,例如 lambda 或 cond 即便没有 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 Reference的Sequencing: begin, begin0, and begin-for-syntax一节中也提供了begin0的文档。
(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 按条件作用:when 与 unless
The Racket Reference的Guarded Evaluation: when and unless一节中也提供了when 与 unless的文档。
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)
****
***
**
*