The Racket Guide阅读笔记,chapter 10.
10 Exceptions and Control
10.1 Exceptions
异常使用with-handler来捕获
(with-handlers ([exn:fail:contract:divide-by-zero?
(lambda (exn) +inf.0)])
(/ 1 0))
在division by zero异常发生时将值替换成+inf.0
。
error
可以用来直接抛出异常:
> (with-handlers ([exn:fail? (lambda (exn) 'air-bag)])
(error "crash!"))
'air-bag
exn:fail:contractr:divide-by-zero
以及exn:fail
都是exn
结构的子类型。
抛出异常的时候也可以不跑出exn相关的值:(raise 2)
使用(lambda (v) #t)
可以捕获所有异常:
> (with-handlers ([(lambda (v) #t) (lambda (v) 'oops)])
(car 17))
'oops
一般情况下,捕获exn:fail?
即可处理常见场景:
> (with-handlers ([exn:fail? (lambda (v) 'oops)])
(break-thread (current-thread)) ; simulate Ctl-C
(car 17))
额外的工具
exn-message
提取异常的错误信息exn-continuation-marks
提取异常的回溯信息
10.2 Prompts and Aborts
call-with-continuation-prompt
在prompt之下对表达式进行计算。default-continuation-prompt-tag
返回到prompt。abort-current-continuation
逃逸到最近的prompt。
> (define (escape v)
(abort-current-continuation
(default-continuation-prompt-tag)
(lambda () v)))
> (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (+ 1 (escape 0)))))))
0
异常的底层是通过prompt和abort来实现的。
10.3 Continuations
continuation(通量)是一个值,可表示求值的上下文。 call-with-composable-continuation
可以捕捉当前的continuation。
> (define saved-k #f)
> (define (save-it!)
(call-with-composable-continuation
(lambda (k) ; k is the captured continuation
(set! saved-k k)
0)))
> (+ 1 (+ 1 (+ 1 (save-it!))))
3
> (saved-k 0)
3
> (saved-k 10)
13
> (saved-k (saved-k 0))
6
scheme传统上使用call-with-current-continuation
或者call/cc
来捕获continuation。但是此操作会在执行之前先abort然后再恢复。使用call-with-composable-continuation
则不须由此顾虑。
11 Iterations and Comprehensions
for族语法形式可以支持在序列之上的迭代。List,vector,string,byte string,input port甚至哈希表这些都可以当作序列。in-range之流的构造函数可以为序列提供更多可能。
for语法格式如下:
(for ([id sequence-expr] ...)
body ...+)
一个例子:
(for ([i '(1 2 3)])
(display i))
for/list
和for类似,不过会把结果累积成一个连对,例子:
> (for/list ([i '(1 2 3)])
(* i i))
'(1 4 9)
11.1 Sequence Constructors
in-range
产生一个数字序列:
> (for ([i (in-range 1 4 1/2)])
(printf " ~a " i))
1 3/2 2 5/2 3 7/2
一个类似的函数时in-naturals,产生从0来时的自然数,无上限。
stop-before和stop-after可以使用一个判定诀来决定结束位置:
> (for ([i (stop-before "abc def"
char-whitespace?)])
(display i))
abc
像in-list, in-vector以及in-string这些跟in-range相似,不过会要求特定的输入数据类型。
11.2 for and for*
for其实可以列举多个序列:
> (for ([i (in-range 1 4)]
[chapter '("Intro" "Details" "Conclusion")])
(printf "Chapter ~a. ~a\n" i chapter))
Chapter 1. Intro
Chapter 2. Details
Chapter 3. Conclusion
迭代的次数取决于最短的那个序列。
for*与for相同的地方时也可以指定多个序列,不同的地方时迭代时按照序列嵌套的。
使用#:when关键字可以在迭代的过程中进行筛选:
> (for* ([book '("Guide" "Reference")]
[chapter '("Intro" "Details" "Conclusion")]
#:when (not (equal? chapter "Details")))
(printf "~a ~a\n" book chapter))
如果指定了多个#:when,那么每个#:when之间的内容时嵌套的而不是并行的,对于for循环也是如此。
#:unless关键字的效用和#:when在选择上正好相反。
11.3 for/list and for*/list
for/list和for类似,不过会以list的方式返回结果。 for*/list和for*就不那么类似了,它会返回平整后的结果,而不是嵌套的结果。
11.4 for/vector and for*/vector
略
11.5 for/and and for/or
for/and在迭代结果上执行and操作,当遇到#f的时候则停止迭代。
11.6 for/first and for/last
for/first返回有效迭代的第一个值。
for/last返回有效迭代的最后一个值。
11.7 for/fold and for*/fold
将迭代结果以某种形式合并在一起:
(for/fold ([accum-id init-expr] ...)
(clause ...)
body ...+)
例子:
> (for/fold ([len 0])
([chapter '("Intro" "Conclusion")])
(+ len (string-length chapter)))
15
11.8 Multiple-Valued Sequences
迭代某些序列的时候需要处理多个值,比如把一个哈希表当作序列的时候。
> (for ([(k v) #hash(("apple" . 1) ("banana" . 3))])
(printf "~a count: ~a\n" k v))
> (for*/list ([(k v) #hash(("apple" . 1) ("banana" . 3))]
[(i) (in-range v)])
k)
'("apple" "banana" "banana" "banana")
11.9 Breaking an Iteration
在for迭代中的子句以及体例中都可以插入#:break或者#:final。前者只要校验为真就立马退出,后者则会执行最后一次迭代后退出。
11.10 Iteration Performance
for为通用而涉及,所以可能需要花额外的判定在数据类型上,从而降低信息。一种改善的办法时主动提供信息从而减少for在判断上的消耗。具体查看文档。
(本篇完)