11迭代和包含

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*/andfor*/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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容