将列表排序为子列表

时间:2015-04-12 01:13:28

标签: scheme racket

我试图创建一个程序来对列表进行排序,然后将列表中的每个部分分组到单独的列表中并将其输出到列表列表中。这是一张应该更清晰的检查:

> (sort-lists > '())
empty

> (sort-lists < '(1 2 3))
(list (list 1 2 3))

> (sort-lists >= '(2 2 2 2))
(list (list 2 2 2 2))

> (sort-lists < '(5 4 3 2 1))
(list (list 5) (list 4) (list 3) (list 2) (list 1))

> (sort-lists < '(1 2 3 4 2 3 4 5 6 1 2 9 8 7))
(list
 (list 1 2 3 4)
 (list 2 3 4 5 6)
 (list 1 2 9)
 (list 8)
 (list 7))

以下是我所拥有的:

(define (sort-lists rel? ls)
  (cond
    [(empty? ls) '()]
    [(rel? (first ls) (first (rest ls)))
     (list (cons (first ls) (sort-lists rel? (rest ls))))]
    [else (cons (first ls) (sort-lists rel? (rest (rest ls))))]))

我对(第一个(休息))部分有问题,因为如果没有先休息,那么它会给出一个错误,与其余的休息相同。

此外,这必须是在ISL +中没有任何帮助的单通函数。任何帮助都会很棒。

有没有办法使用local将解决方案与递归子问题合并到ans变量,然后完成答案。因此,对于(sort-lists < '(1 2 3 4 2 3 4 5 6 1 2 9 8 7)),您将定义ans是运行(sort-lists < '(2 3 4 2 3 4 5 6 1 2 9 8 7))的结果,即'((2 3 4) (2 3 4 5 6) (1 2 9) (8) (7)).

2 个答案:

答案 0 :(得分:7)

我不会把它称为 sort ,就像某种类型的分区一样。您正在尝试收集已根据谓词排序的最长的连续元素序列。我知道你说你必须将这一切都捆绑到一个函数中,但是首先将它作为单独的函数编写,然后将它们合并为一个函数可能要容易得多。

在解决这个问题时,将其分解为子任务可能会有所帮助。首先,在最高级别,当列表进入时,有一些升序元素的初始前缀,然后是其后的元素。结果应该是第一个前缀的列表,然后是处理其余元素的结果。这给了我们这样的结构:

(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 * ,它执行顺序绑定, list **,与** cons 相同。还有一个对我们尚未定义的函数 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 < '(1 2 3 4 2 3 4 5)) ;=> ((1 2 3 4) (2 3 4 5))
但是,它不是一个功能。要实现这一目标,您需要将 ordered-prefix 的定义纳入 slice 的定义中。您可以使用绑定其他函数中的函数,例如:

(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 做到这一点,它允许函数引用自己。 E.g:

(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 现在已在
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)的值相同。你应该改变这一点,但为了清楚起见,我想保留原样。

唯一的性能考虑是这不是尾递归,因此您使用了更多的堆栈空间。要解决这个问题,您需要将递归转换为反向构建列表的表单,然后在返回时将其反转。这就是我在原版中所做的事情(您仍然可以查看编辑历史记录),但对于看似是学术活动的内容来说可能有些过分。

答案 1 :(得分:1)

您想将列表分成最长的升序数字。而要在ISL +中完成,只需一次通过

这是通过逻辑编程伪代码完成的(嗯,Prolog):

runs([],  [[] ]).
runs([A], [[A]]).
runs([A,B|C], R) :- 
   (   A > B   ->  runs([B|C],  S  ), R=[[A  ]|S]   
   ;   true    ->  runs([B|C],[D|S]), R=[[A|D]|S]   ).

这在类似Scheme的伪代码(嗯,完整的Racket)中也一样:

(define (runs xs)
   (match xs 
     ((list )  '(()))
     ((list A)  (list (list A)))
     ((list A B C ...)
        (cond
          ((> A B)
             (let ([S (runs (cons B C))])
                (cons (list A) S)))
          (else
             (let ([d_s (runs (cons B C))])
                (let ([D (car d_s)]
                      [S (cdr d_s)])
                   (cons (cons A D) S))))))))

剩下要做的就是让它适合ISL +语言。我不知道+代表什么,但是在“有lambda的中级学生”语言中肯定会有一个lambda构造。这使我们可以将嵌套let的分段分配模拟为

          ( (lambda (d_s)
               ( (lambda (D S)
                     (cons (cons A D) S))
                 (car d_s)
                 (cdr d_s)))
            (runs (cons B C)))