for语法体系用来处理序列上的迭代。列表,向量,字符串,比特字符串,输入端口,哈希表都能被当做序列使用。构造函数比如in-range也能缠身序列。
for变种以各种方式积累迭代结果,但是它们的语法形式相同。
(for ([id sequence-expr] ...)
body ...+)
for循环迭代通过sequence-expr产生的序列。序列的每一个元素,都会绑定到id,然后执行body代码。
>(for ([i '(1 2 3)])
(display i))
123
>(for ([i "abc"])
(printf "~a..." i))
a...b...c...
>(for ([i 4])
(display i))
0123
for/list
是一种更加racket风格的变种。它会累积方法体的结果到一个列表,而不是单纯执行方法体。更加专业的术语,for/list
实现了一种列表包含。
>(for/list ([i '(1 2 3)]
(* i i))
'(1 4 9)
>(for/list ([i "abc"])
i)
'(#\a #\b #\c)
>(for/list ([i 4])
i)
'(0 1 2 3)
for
语法的完全形式可以同时平行的迭代多个序列,for*
内嵌迭代而不是平行的执行。for系列的语法辩题,都能使用条件来过滤迭代项。
11.1 序列构造器
in-range
函数产生了一个数字的序列,可以设置一个可选的开始值,一个结束值(结束前的最后一个),一个可选的步进值。使用一个非负整数k直接产生一个序列的代码(in-range k)
。
>(for ([i 3])
(display i))
012
>(for ([i (in-range 3)])
(display i))
012
>(for ([i (in-range 1 4])
(display i))
123
>(for ([i (in-range 1 4 2)])
(display i))
13
>(for ([i (in-range 1 4 1/2])
(printf "~a" i))
1 3/2 5/2 3 7/2
in-naturals
类似,处理开始数字必须是一个非负整数(默认是0),步进值总是1,但是没有上限。它不会终止除非异常或者其它的跳出。
>(for ([i (in-naturals)])
(if (= i 10)
(error "too much")
(display i)))
stop-before
,stop-after
函数通过一个序列和一个谓词判断产生一个新的序列。产生的新序列和原来的序列一样,但是在谓词判断返回true以前或者以后马上停止。
>(for ([i (stop-before "abc def"
char-whitespace?)])
(display i))
abc
in-list
,in-vector
,in-string
明确了产生序列的类型。如果赋值了一个错误类型,会抛出一个异常。因为避免了运行时的类型分发,代码的效率更高。
11.2for和for*
更加复杂的for语句
>(for (clause ...)
body ...+)
clause= [id sequence-expr]
| #:when boolean-expr
| #:unless boolean-expr
当有多个[id sequence-expr]子句,它会平行遍历多个序列。当其中一个平行序列结束,整个遍历就会结束。利用这种特性,结合in-naturals
这种产生无限序列的构造器,可以产生索引。
>(for ( [i (in-naturals 1)]
[chapter '("Intro" "Details" "Conclusion")])
(printf "Chapter ~a. ~a\n" i chapter))
Chapter 1. Intro
Chapter 2. Details
Chapter 3. Conclusion
for*
和for类似,但是内嵌迭代而不是平行。
for之于for正如let之于let。
#:when boolean-expr
形式只有在返回true时,方法体才会执行。在一个for形式中,使用when会造成迭代内嵌,即使在for里面也是这样。
#:unless boolean-expr
与when相反。
11.3for/list 和 for*/list
for/list
和for有一样的语法形式,执行方法体获得一个新列表。
使用#:when
可以裁剪结果列表。
>(for/list ([i (in-naturals 1)]
[chapter '("Intro" "Details" "Conclusion")]
#:when (odd? i))
chapter)
'("Intro" "Conclusion")
#:when
的裁剪功能在for/list
更加有用。原始的when表达式会产生一个#<void>值而不是忽略返回值。
'for/list'形式和for*
类似,内嵌多个迭代。
>(for/list ([book '("Guide" "Ref.")]
[chapter '("Intro" "Details")])
(string-append book " " chapter))
'("Guide Intro" "Guide Details" "Ref. Intro" "Ref. Details")
for*/list和for/list的内嵌形式并不相同。内嵌的for/list会产生一个列表的列表,而不是展开的列表。它更像#:when
形式造成的内嵌。
11.4 fro/vector 和for*/vector
for/vector
语法和for/list
相似,只是构造向量而不是列表。它们都能指定#:length
参数。
>(let ([chapters '("Intro" "Details" "Conclusion")])
(for/vector #:length (length chapters) ([i (in-naturals 1)]
[chanpter chapters])
(string-append (number->string i) ". " chapter)))
'#("1. Intro" "2. Details" "3. Conclusion")
如果length参数被提供,当向量填满或者迭代完成,迭代自动完成。如果length超过请求的迭代,剩下的使用make-vector使用初始化。
11.5for/and和for/or
for/and
形式使用and合并迭代,如果结果是#f则停止。
>(for/and ([chapter '("Intro" "Details" "Conclusion")])
(equal? chapter "Intro"))
#f
for/or
形式使用or合并迭代,如果结果是#t则停止。
>(for/or ([chanpter '("Intro" "Details" "Conclusion")])
(equal? chapter "Intro"))
#t
for*/and
和for*/or
提供相同的功能但是内嵌迭代。
11.6 for/first和for/last
for/first
形式返回第一次方法体执行的结果,忽略其它迭代。#:when
子句在这种形式里更有用。
>(for/list ([chanpter '("Intro" "Details" "Conclusion" "Index")]
#:when (not (equal? chapter "Intro")))
chanpter)
"Details"
如果方法体被执行了0次,结果是#f。
for/last
运行迭代,返回最后一个迭代的值。如果没有迭代,则返回#f。
>(for/last ([chapter '("Intro" "Details" "Conclusion" "Index")
#:when (not (equal? chapter "Index"))])
chapter)
"Conclusion"
for*/first
和'for*/last'提供了类似的内嵌的功能
11.7for/fold和for*/fold
for/fold
是一种很普遍的用来收集迭代结果的方式。它的语法和for只有稍微的不同,因为累加器变量必须在开始的时候定义。
(for/fold ([accum-id init-expr] ...)
(clause ...)
body ..+)
在简单的情况下只有一个[accum-id init-expr]被提供,函数的运行结果则是最后的accum-id值,它开始的初始值是init-expr。
>(for/fold ([len 0])
([chanpter '("Intro" "Conclusion")])
(+ len (string-length chapter)))
15
>(fro/fold ([prev #f])
([i (in-naturals 1)]
[chapter '("Intro" "Details" "Details" "Conclusion")]
#:when (not (equal? chanpter prev)))
(printf "~a. ~a\n" i chanpter)
chapter)
1. Intro
2. Details
4. Conclusion
"Conclusion"
当多个累加器被指定,方法体最后必须产生多个值,与每个累加器对应。for/fold
本身也会返回多个值。
>(for/fold ([prev #f]
[counter 1])
([chapter '("Intro" "Details" "Details" "Conclusion")]
#:when (not (equal? chapter prev)))
(printf "~a. ~a\n" counter chapter)
(values chapter
(add1 counter)))
1. Intro
2. Details
3. Conclusion
"Conclusion"
4
11.8多值序列
和函数和表达式产生多值一样,序列的迭代也能产生多个值。比如,哈希表就可以长生一个两个值的迭代。
和let-values可以绑定多个结果到多个标识符,for也可以绑定多值的序列元素到多个迭代标识
>(for ([k v] #hash(("apple" .1) ("banana" . 3))])
(printf "~a count: ~a\n" k v))
apple count: 1
banana count: 3
多值绑定能在多个for变种里使用。
>(for*/list ([k v) #hash(("apple" . 1) ("banana" . 3))]
[(i) (in-range v)])
'("apple" "banana" "banana" "banana")
11.9跳出迭代
(for (clause ...)
body-or-break ... body)
clause=[id sequence-expr]
|#:when boolean-expr
|#:unless boolean-expr
|break
body-or-break=body
|break
break=#:break boolean-expr
|#:final boolean-expr
#:break
和#:final
能使用在绑定语句和方法体里。在绑定语句里,#:break
作用和'#:unless'很像,但是如果boolean-expr是true,所有序列迭代都会停止。在方法体里,#:break
和在语句里有着相同的效果,它会阻断当前迭代以后的方法体执行。
>(for ([book '("Cuide" "Story" "Reference")]
#:unless (equal? book "Story")
[chapter '("intor" "Details" "Conclusion")])
(printf "~a ~a\n" book chapter))
Guide Intro
Guide Details
Guide Conclusion
Reference Intro
Reference Details
Reference Conclusion
使用#:break
则会终止整个迭代
>(for ([book '("Guide" "Story" "Reference")]
#:break (equal? book "Story")
[chapter '("Intro" "Details" "Conclusion")])
(printf "~a ~a\n" book chapter))
Guide Intro
Guide Details
Guide Conclusion
>(for* ([book '("Guide" "Story" "Reference")]
[chapter '("Intro" "Details" "Conclusion")])
#:break (and (equal? book "Story")
(equal? chanpter "Conclusion"))
(printf "~a ~a\n" book chapter))
Guide Intro
Guide Details
Guide Conclusion
Story Intro
Story Details
#:final
语句和#:break
类似,但是不会马上终止迭代。它允许执行最后一次。
>(for* ([book '("Guide" "Story" "Reference")]
[chapter '("Intro" "Details" "Conclusion")])
#:final (and (equal? book "Story")
(equal? chapter "Conclusion")
(printf "~a ~a\n" book chapter))
Guide Intro
Guide Details
Guide Conclusion
Story Intro
Story Details
Story Conclusion
>(for ([book '("Guide" "Story" "Reference")]
#:final (equal? book "Story")
[chanpter '("Intro" "Details" "Conclusion")])
(printf "~a ~a\n" book chapter))
Guide Intro
Guide Details
Guide Conclusion
Story Intro
11.10迭代效率
理想情况下,for迭代运行和你自己手动实现的递归调用一样快。但是手写的循环,一般指定了一种特定的数据,像列表。在这种情况下,手写循环直接使用car,cdr,而不是处理各种形式的序列然后分发他们到合适的迭代器。
for形式也可以达到手写循环的效率当提供足够多的迭代信息。特别的,子句应该有其中一个fast-clause形式。
fast-clause=[id fast-seq]
|[(id) fast-seq]
|[(id id) fast-indexed-seq]
|[(id ...) fast-parallel-seq]
fast-seq=(in-range expr)
|(in-range expr expr)
|(in-range expr expr expr)
|(in-naturals)
|(in-naturals expr)
|(in-list expr)
|(in-vector expr)
|(in-string expr)
|(in-bytes expr)
|(in-value expr)
|(stop-before fast-seq predicate-expr)
|(stop-after fast-seq predicate-expr)
fast-indexed-seq=(in-indexed fast-seq)
|(stop-before fast-indexed-seq predicate-expr)
|(stop-after fast-indexed-seq predicate-expr)
fast-parallel-seq=(in-parallel fast-seq ...)
|(stop-before fast-parallel-seq predicate-expr)
|(stop-after fast-parallel-seq predicate-expr)