今天在优快云上丢了两篇文章,也促使我开始注意博客系统的使用方法。现在发现的问题有:
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-ref
和length
列表的长度等于元素的个数,而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
。