我不会真正称其为排序,而是某种类型的分区。您正在尝试收集已根据谓词排序的最长连续元素序列。我知道您说过您必须将所有这些都捆绑到一个函数中,但是首先将其编写为单独的函数,然后将它们组合成一个可能要容易得多。
在解决此问题时,将其分解为子任务可能会有所帮助。首先,在最高级别,当列表进入时,有一些升序元素的初始前缀,然后是其余元素。结果应该是第一个前缀的列表,然后是处理其余元素的结果。这给了我们这样的结构:
(define (slice predicate lst)
(if (empty? lst)
;; If lst is empty, then there no contiguous
;; subsequences within it, so we return '()
;; immediately.
'()
;; Otherwise, there are elements in lst, and we
;; know that there is definitely a prefix and
;; a tail, although the tail may be empty. Then
;; the result is a list containing the prefix,
;; and whatever the sliced rest of the list is.
(let* ((prefix/tail (ordered-prefix predicate lst))
(prefix (first prefix/tail))
(tail (second prefix/tail)))
(list* prefix (slice predicate tail)))))
我希望该函数中的逻辑相对清晰。唯一可能有点不寻常的是执行顺序绑定的 let* 和与 **cons 相同的 list**。还有一个我们还没有定义的函数的引用,ordered-prefix。它的任务是返回一个包含两个值的列表;第一个是列表的有序前缀,第二个是该前缀之后的列表尾部。现在我们只需要编写那个函数:
(define (ordered-prefix predicate lst)
(cond
;; If the list is empty, then there's no prefix,
;; and the tail is empty too.
((empty? lst)
'(() ()))
;; If the list has only one element (its `rest` is
;; empty, then the prefix is just that element, and
;; the tail is empty.
((empty? (rest lst))
(list (list (first lst)) '()))
;; Otherwise, there are at least two elements, and the
;; list looks like (x y zs...).
(else
(let ((x (first lst))
(y (second lst))
(zs (rest (rest lst))))
(cond
;; If x is not less than y, then the prefix is (x),
;; and the tail is (y zs...).
((not (predicate x y))
(list (list x) (list* y zs)))
;; If x is less than y, then x is in the prefix, and the
;; rest of the prefix is the prefix of (y zs...).
(else
(let* ((prefix/tail (ordered-prefix predicate (list* y zs)))
(prefix (first prefix/tail))
(tail (second prefix/tail)))
(list (list* x prefix) tail))))))))
现在,这足以让 slice 工作:
(slice < '()) ;=> ()
(slice < '(1 2 3 4 2 3 4 5)) ;=> ((1 2 3 4) (2 3 4 5))
不过,这并不是一个功能。为此,您需要将 ordered-prefix 的定义放入 slice 的定义中。您可以使用 let 在其他函数中绑定函数,例如:
(define (repeat-reverse lst)
(let ((repeat (lambda (x)
(list x x))))
(repeat (reverse lst))))
(repeat-reverse '(1 2 3)) ;=> ((3 2 1) (3 2 1))
但是,这不适用于 ordered-prefix,因为 ordered-prefix 是递归的;它需要能够引用自己。您可以使用 letrec 来做到这一点,它允许函数引用自己。例如:
(define (repeat-n-reverse lst n)
(letrec ((repeat-n (lambda (x n)
(if (= n 0)
'()
(list* x (repeat-n x (- n 1)))))))
(repeat-n (reverse lst) n)))
(repeat-n-reverse '(1 2 3) 3) ;=> ((3 2 1) (3 2 1) (3 2 1))
(repeat-n-reverse '(x y) 2) ;=> ((y x) (y x))
(repeat-n-reverse '(a b c d e) 0) ;=> ()
好的,现在我们准备好将它们放在一起。 (由于 ordered-prefix 现在定义在 within slice,它已经可以访问谓词,我们可以将它从参数列表中删除,但仍然使用它。)
(define (slice predicate lst)
(letrec ((ordered-prefix
(lambda (lst)
(cond
((empty? lst)
'(() ()))
((empty? (rest lst))
(list (list (first lst)) '()))
(else
(let ((x (first lst))
(y (second lst))
(zs (rest (rest lst))))
(cond
((not (predicate x y))
(list (list x) (list* y zs)))
(else
(let* ((prefix/tail (ordered-prefix (list* y zs)))
(prefix (first prefix/tail))
(tail (second prefix/tail)))
(list (list* x prefix) tail))))))))))
(if (empty? lst)
'()
(let* ((prefix/tail (ordered-prefix lst))
(prefix (first prefix/tail))
(tail (second prefix/tail)))
(list* prefix (slice predicate tail))))))
这也是相对有效的。它不会分配任何不必要的数据,除了为了清楚起见我使用 (list* y zs) 的地方,那里的值与 (rest lst) 相同。您可能应该更改它,但为了清楚起见,我想保持原样。
唯一的性能考虑是这不是尾递归,所以你使用了更多的堆栈空间。为了解决这个问题,您需要将递归转换为反向构建列表的形式,然后在返回时将其反转。这就是我在原版中所做的(您仍然可以查看编辑历史记录),但对于看似学术的练习来说,这可能有点矫枉过正。