P01 (*) 获取列表最后一个元素【重补】

来源:互联网 发布:php 字符串匹配查找 编辑:程序博客网 时间:2024/06/02 16:52

今天在CSDN上丢了两篇文章,也促使我开始注意博客系统的使用方法。现在发现的问题有:
1. 草稿箱好像只能保存一篇文章,其他文章我怎么都保存不到草稿箱
2. 编辑文章不保存或不发表,文章莫名从文章列表里“丢失“

不知是产品缺陷,还是有意为之,我先把文章补上,这是其一。

进入问题之前,先说一说列表是什么,我们可以类比C/C++中的链表。按R7RS中所说,它可递归定义为:(1) 要么为空,即'(),(2)要么是一个pair,其cdr是列表。pair是一个二元对偶,跟std::pair类似。可用标准函数list?判断是否是一个有效列表。

以后的问题除特别说明,参数ls都为有效列表。

进入问题:
Example:

sash> (my-last '(a b c d))sash> d

这是原先我给出的三种解决方案:
(1)自己造轮子
Scheme内置的列表本质上是单链表,要查找最后一个元素,只能依次遍历列表,找到其cdr'()的列表,并返回其car。如

(define my-last    (lambda (ls)      (if (null? ls)          (raise 'not-found)          (let ([t (cdr ls)])            (if (null? t)                (car ls)                (my-last t))))))

如果列表为空,应返回一个值表示失败。但列表中的值是任意的,不好确定合适的值,这里用raise抛出异常。

(2)使用list-reflength
列表的长度等于元素的个数,而list-ref索引从0开始,结合起来:

(define my-last    (lambda (ls)      (let ([len (length ls)])        (if (zero? len)            (raise 'not-found)            (list-ref ls (- len 1))))))

(3)使用reverse
可以先反向排列列表,这样所得列表的第一个元素即为所求。

(define my-last    (lambda (ls)      (if (null? ls)          (raise 'not-found)          (car (reverse ls)))))

更新

在回顾的时候,发现方法(1)有逻辑冗余。这里在关键语句加上数字标签,帮助我们分析。

(define my-last    (lambda (ls)      (if (null? ls)               (1)          (raise 'not-found)          (let ([t (cdr ls)])            (if (null? t)          (2)                (car ls)                (my-last t))))))   (3)

看以下(my-last '(1 2))这个调用的语句执行顺序,会得到
执行序列(1)–>(2)–>(3)->(1)–>(2)。(3)前后都是判断列表是否为空,(2)判断了列表t,在新的递归中,t替换了ls,(1)判断了ls,实际上同一个列表t被判断了两次。
避免冗余,代码修改如下:

(define my-last  (lambda (ls)    (if (null? ls)        (raise 'not-found)        (let loop ([h (car ls)]                   [t (cdr ls)])          (if (null? t)              h              (loop (car t)                    (cdr t)))))))

出现之前的原因在于,混淆前提和主体。第一个null?判断在于给整个递归提供一个健康的环境,下面的递归主体其实就是循环不变式:若cdr为空,返回car

1 0
原创粉丝点击