关于length

写Scheme代码时,经常要判断表的长度。长度为0时一个操作,长度为1时,另一个操作,长度为2时,第三种操作 ...。
最容易想到的方法是
(define (length=? m l)
  (= m (length l)))
但这个 length=? 很低效。它要先计算表的长度n。length 的复杂度是O (n),。这个函数的复杂度也是O (n)。

改进一

实际上表的长度等不等于m,在计算表的长度*时*,就可以确定,不必等到计算表的长度*后*。【不多不少原则】
length 可以这样写
(define (length l)
  (if (null? l)
      0
      (+ 1 (length (cdr l)))))
要在计算 length 的时候就判断其长度是否等于 m ,就可以这样写
(define (length=? m l)
  (if (null? l)
      (zero? m)
      (length=? (- m 1) (cdr l))))

[根据 length 来写 length=? 是很简单的小技巧]

改进二
上面的改进是考察表的长度得到的。这里我们把另外一参数 m 也考虑进去。高效的 length=? 的复杂度应该是 min (m,n)。也就是说当m比n小时,m步就可以判定了。
(define (length=? m l)
  (cond ((null? l) (zero? m))
        ((zero? m) #f)
        (else (length=? (- m 1) (cdr l)))))


类似地,要判断表的长度是否相等就可以这样

(define (same-length? l1 l2)
  (cond ((null? l1) (null? l2))
        ((null? l2) #f)
        (else (same-length? (cdr l1) (cdr l2)))))

如果要判断两个以上的表的长度是否相等呢?
方法应该还是一样的。

1. 如果其中一个表是空表,看其他表是否也都是空表。
2. 否则去掉所有表的头部,递归。

这就要两个辅助函数 any? 和 all?
(define (any? p? ls)
  (define anyloop
    (lambda (ls)
      (if (null? ls)
          #f
          (if (p? (car ls))
              #t
              (anyloop (cdr ls))))))
  (anyloop ls))

(define (all? p? ls)
  (define allloop
    (lambda (ls)
      (if (null? ls)
          #t
          (if (not (p? (car ls)))
              #f
              (allloop (cdr ls))))))
  (allloop ls))

(define (same-length? l1 l2 . ls)
  (define args (cons l1 (cons l2 ls)))
  (cond ((any? null? args)
         (all? null? args))
        (else 
         (apply same-length?
                (map cdr args)))))

不过这个函数的复杂度,我就不愿意分析了,因为不会呵呵。
length=? 也可以作类似扩展。

 

posted @ 2014-10-10 12:25  ysc  阅读(279)  评论(0编辑  收藏  举报