for語法體系用來處理序列上的迭代。列表,向量,字符串,比特字符串,輸入端口,哈希表都能被當(dāng)做序列使用。構(gòu)造函數(shù)比如in-range也能纏身序列。
for變種以各種方式積累迭代結(jié)果,但是它們的語法形式相同。
(for ([id sequence-expr] ...)
body ...+)
for循環(huán)迭代通過sequence-expr產(chǎn)生的序列。序列的每一個(gè)元素,都會(huì)綁定到id,然后執(zhí)行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風(fēng)格的變種。它會(huì)累積方法體的結(jié)果到一個(gè)列表,而不是單純執(zhí)行方法體。更加專業(yè)的術(shù)語,for/list
實(shí)現(xiàn)了一種列表包含。
>(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
語法的完全形式可以同時(shí)平行的迭代多個(gè)序列,for*
內(nèi)嵌迭代而不是平行的執(zhí)行。for系列的語法辯題,都能使用條件來過濾迭代項(xiàng)。
11.1 序列構(gòu)造器
in-range
函數(shù)產(chǎn)生了一個(gè)數(shù)字的序列,可以設(shè)置一個(gè)可選的開始值,一個(gè)結(jié)束值(結(jié)束前的最后一個(gè)),一個(gè)可選的步進(jìn)值。使用一個(gè)非負(fù)整數(shù)k直接產(chǎn)生一個(gè)序列的代碼(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
類似,處理開始數(shù)字必須是一個(gè)非負(fù)整數(shù)(默認(rèn)是0),步進(jìn)值總是1,但是沒有上限。它不會(huì)終止除非異常或者其它的跳出。
>(for ([i (in-naturals)])
(if (= i 10)
(error "too much")
(display i)))
stop-before
,stop-after
函數(shù)通過一個(gè)序列和一個(gè)謂詞判斷產(chǎn)生一個(gè)新的序列。產(chǎn)生的新序列和原來的序列一樣,但是在謂詞判斷返回true以前或者以后馬上停止。
>(for ([i (stop-before "abc def"
char-whitespace?)])
(display i))
abc
in-list
,in-vector
,in-string
明確了產(chǎn)生序列的類型。如果賦值了一個(gè)錯(cuò)誤類型,會(huì)拋出一個(gè)異常。因?yàn)楸苊饬诉\(yùn)行時(shí)的類型分發(fā),代碼的效率更高。
11.2for和for*
更加復(fù)雜的for語句
>(for (clause ...)
body ...+)
clause= [id sequence-expr]
| #:when boolean-expr
| #:unless boolean-expr
當(dāng)有多個(gè)[id sequence-expr]子句,它會(huì)平行遍歷多個(gè)序列。當(dāng)其中一個(gè)平行序列結(jié)束,整個(gè)遍歷就會(huì)結(jié)束。利用這種特性,結(jié)合in-naturals
這種產(chǎn)生無限序列的構(gòu)造器,可以產(chǎn)生索引。
>(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類似,但是內(nèi)嵌迭代而不是平行。
for之于for正如let之于let。
#:when boolean-expr
形式只有在返回true時(shí),方法體才會(huì)執(zhí)行。在一個(gè)for形式中,使用when會(huì)造成迭代內(nèi)嵌,即使在for里面也是這樣。
#:unless boolean-expr
與when相反。
11.3for/list 和 for*/list
for/list
和for有一樣的語法形式,執(zhí)行方法體獲得一個(gè)新列表。
使用#:when
可以裁剪結(jié)果列表。
>(for/list ([i (in-naturals 1)]
[chapter '("Intro" "Details" "Conclusion")]
#:when (odd? i))
chapter)
'("Intro" "Conclusion")
#:when
的裁剪功能在for/list
更加有用。原始的when表達(dá)式會(huì)產(chǎn)生一個(gè)#<void>值而不是忽略返回值。
'for/list'形式和for*
類似,內(nèi)嵌多個(gè)迭代。
>(for/list ([book '("Guide" "Ref.")]
[chapter '("Intro" "Details")])
(string-append book " " chapter))
'("Guide Intro" "Guide Details" "Ref. Intro" "Ref. Details")
for*/list和for/list的內(nèi)嵌形式并不相同。內(nèi)嵌的for/list會(huì)產(chǎn)生一個(gè)列表的列表,而不是展開的列表。它更像#:when
形式造成的內(nèi)嵌。
11.4 fro/vector 和for*/vector
for/vector
語法和for/list
相似,只是構(gòu)造向量而不是列表。它們都能指定#:length
參數(shù)。
>(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參數(shù)被提供,當(dāng)向量填滿或者迭代完成,迭代自動(dòng)完成。如果length超過請(qǐng)求的迭代,剩下的使用make-vector使用初始化。
11.5for/and和for/or
for/and
形式使用and合并迭代,如果結(jié)果是#f則停止。
>(for/and ([chapter '("Intro" "Details" "Conclusion")])
(equal? chapter "Intro"))
#f
for/or
形式使用or合并迭代,如果結(jié)果是#t則停止。
>(for/or ([chanpter '("Intro" "Details" "Conclusion")])
(equal? chapter "Intro"))
#t
for*/and
和for*/or
提供相同的功能但是內(nèi)嵌迭代。
11.6 for/first和for/last
for/first
形式返回第一次方法體執(zhí)行的結(jié)果,忽略其它迭代。#:when
子句在這種形式里更有用。
>(for/list ([chanpter '("Intro" "Details" "Conclusion" "Index")]
#:when (not (equal? chapter "Intro")))
chanpter)
"Details"
如果方法體被執(zhí)行了0次,結(jié)果是#f。
for/last
運(yùn)行迭代,返回最后一個(gè)迭代的值。如果沒有迭代,則返回#f。
>(for/last ([chapter '("Intro" "Details" "Conclusion" "Index")
#:when (not (equal? chapter "Index"))])
chapter)
"Conclusion"
for*/first
和'for*/last'提供了類似的內(nèi)嵌的功能
11.7for/fold和for*/fold
for/fold
是一種很普遍的用來收集迭代結(jié)果的方式。它的語法和for只有稍微的不同,因?yàn)槔奂悠髯兞勘仨氃陂_始的時(shí)候定義。
(for/fold ([accum-id init-expr] ...)
(clause ...)
body ..+)
在簡(jiǎn)單的情況下只有一個(gè)[accum-id init-expr]被提供,函數(shù)的運(yùn)行結(jié)果則是最后的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"
當(dāng)多個(gè)累加器被指定,方法體最后必須產(chǎn)生多個(gè)值,與每個(gè)累加器對(duì)應(yīng)。for/fold
本身也會(huì)返回多個(gè)值。
>(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多值序列
和函數(shù)和表達(dá)式產(chǎn)生多值一樣,序列的迭代也能產(chǎn)生多個(gè)值。比如,哈希表就可以長生一個(gè)兩個(gè)值的迭代。
和let-values可以綁定多個(gè)結(jié)果到多個(gè)標(biāo)識(shí)符,for也可以綁定多值的序列元素到多個(gè)迭代標(biāo)識(shí)
>(for ([k v] #hash(("apple" .1) ("banana" . 3))])
(printf "~a count: ~a\n" k v))
apple count: 1
banana count: 3
多值綁定能在多個(gè)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,所有序列迭代都會(huì)停止。在方法體里,#:break
和在語句里有著相同的效果,它會(huì)阻斷當(dāng)前迭代以后的方法體執(zhí)行。
>(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
則會(huì)終止整個(gè)迭代
>(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
類似,但是不會(huì)馬上終止迭代。它允許執(zhí)行最后一次。
>(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迭代運(yùn)行和你自己手動(dòng)實(shí)現(xiàn)的遞歸調(diào)用一樣快。但是手寫的循環(huán),一般指定了一種特定的數(shù)據(jù),像列表。在這種情況下,手寫循環(huán)直接使用car,cdr,而不是處理各種形式的序列然后分發(fā)他們到合適的迭代器。
for形式也可以達(dá)到手寫循環(huán)的效率當(dāng)提供足夠多的迭代信息。特別的,子句應(yīng)該有其中一個(gè)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)