2.3列表,迭代和递归

Racket是lisp的方言,它起初的意思是 “LISt Processor”。 内建的list数据结构保留了这种语言特征。
list函数接受多个值并且返回包含多个值的列表。list在repl里面打印有一个<code>'</code>和一对括号。

2.3.1预定义list循环

recket包含了很多函数迭代list。
map使用每一个元素的结果构造一个新的list
andmap,ormap使用and或者or来联合结果
filter 过滤非#f的元素
map,andmap,ormap.filter都可以接受多个list,所有list都相同长度,且函数必须针对每个列表有一个参数。
foldl 迭代每个元素,并合并迭代的结果到当前值。所以foldl函数需要一个初始化值。

2.3.2 list原始迭代

non-empty list操作
first:返回列表第一个元素
rest:返回剩下的列表
empty 常量,空列表
cons 增加元素到列表头
为了处理一个列表,需要处理空列表和非空列表。因为first和rest只能工作在非空列表上。empty?判断是否是空列表。cons?判断非空列表。
使用这些原始函数,你能构造length函数,map函数。

 (define (my-length lst)
    (cond 
    [(empty? lst) 0]
    [else (+ 1 (my-length (rest lst)))]))

(define (my-map f lst)
    (cond
      [(empty? lst)  empty]
      [else (cons (f (first lst)) 
                       (my-map f (rest lst)))]))

2.3.3 尾递归

my-length和my-map函数都是O(n)的。my-leng 函数需要堆叠(+ 1 ···)直到列表耗尽,然后才开始累加。

(my-length (list "a" "b" "c"))
= (+ 1 (my-length (list "b" "c")))
= (+ 1 (+ 1 (my-length (list "c"))))
= (+ 1 (+ 1 (+ 1 (my-length (list)))))
= (+ 1 (+ 1 (+ 1 0)))
= (+ 1 (+ 1 1))
= (+ 1 2)
= 3

我们更够改进计算方式避免额外的步骤。

(define (my-length lst)
; local function iter:
  (define (iter lst len)
  (cond
     [(empty? lst) len]
     [else (iter (rest lst) (+ len 1))]))
     ; body of my-length calls iter:
     (iter lst 0))

上面函数的调用<code>(iter (list "b" "c") 1) </code>实际上是<code>(iter (list "b") 2) </code>的值,第一个表达式的不需要等待第二个表达式返回。
上面这种求值得行为叫做尾递归优化。但是在reacket里面,它不只是一种优化。它是保证代码运行的手段。
对于my-map函数,也可以修改成尾递归。唯一要注意的是,累积的list是逆序的,你要反转它。可以使用for/list 来改进my-map,它只是一种的语法糖。

(define (my-app f lst)
  (for/list ([i lst])
    (f i)))

2.3.4递归vs迭代

my-length和my-map的例子证明迭代只是一种递归的特殊情况。在很多语言中,使用迭代的形式是很重要的,否则,性能会很差,当数据量很大的时候,还会堆栈溢出。Racket的情况也类似,有时候使用尾递归是很重要的,它可以避免O(n)的空间消耗,因为它值在一个常量空间里执行。
与此同时,递归在racket里面不会导致特别差的性能,也不会产生堆栈的溢出。你可能会导致内存溢出如果执行了太多的上下文,但是耗尽内存代表你调用了大量的深度递归,在其他语言中,这种情况会产生堆栈溢出。基于上面的描述,racket程序员应该拥抱递归而不是避免它们。
比如,从一个列表中移除连续的重复。

(define (remove-dups l)
    (cond
      [(empty? l) empty]
      [(empty? (rest l)) l]
      [else 
        (let ([i (first l)])
          (if (equal? i (first (rest l)))
          (remove-dups (rest l))
          (cons i (remove-dups (rest l)))))]))

总的来说,上面这个函数在输入长度n的列表时消耗O(n)的空间,但是这并没有什么问题,因为它产生O(n)的结果。如果列表恰好重复度比较高,那么结果会小于O(n),而且
remove-dups的使用空间也小于O(n)。这是因为在丢弃重复时,函数直接返回remove-dups的结果,所以产生了尾递归优化。

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

推荐阅读更多精彩内容