【问题标题】:How to improve this mergesort in scheme?如何在方案中改进这种合并排序?
【发布时间】:2009-07-07 06:50:07
【问题描述】:

我是一名 C++ 程序员,我写了这段代码来看看我是否可以在功能上思考 :) 有什么改进的提示吗?

(define (append listOne listTwo)
  (cond
    ((null? listOne) listTwo)
    (else (cons (car listOne) (append (cdr listOne) listTwo)))))

(define (merge listOne listTwo)
  (cond
    ((null? listOne) listTwo)
    ((null? listTwo) listOne)
    ((< (car listOne) (car listTwo))
     (append (cons (car listOne) '())
             (merge (cdr listOne) listTwo)))
    ((= (car listOne) (car listTwo))
     (append (cons (car listOne) '())
             (merge (cdr listOne) listTwo)))
    (else (append (cons (car listTwo) '())
                  (merge listOne (cdr listTwo))))))

(define (mergesort lis)
  (cond
    ((null? lis) '())
    ((null? (cdr lis)) lis)
    (else (merge (cons (car lis) '())
                 (mergesort (cdr lis))))))

(mergesort '(99 77 88 66 44 55 33 11 22 0))

【问题讨论】:

标签: algorithm scheme mergesort


【解决方案1】:

我只看到了一个小的改进:

(append (cons (car listTwo) '())
              (merge listOne (cdr listTwo))))

在任何地方都可以简化为

(cons (car listTwo)
      (merge listOne (cdr listTwo)))

我想你在想类似的东西(在 Python 风格的语法中):

[car(listTwo)] + merge(listOne, cdr(listTwo))

但是cons直接在列表的前面添加了一个item,就像一个函数push,所以就像下面的代码:

push(car(listTwo), merge(listOne, cdr(listTwo)))

最终,额外的附加只会导致每个项目的双重缺点单元分配,所以这没什么大不了的。

另外,我认为如果你让mergesort 更高级,这样你可能会获得更好的性能,以便它保持列表长度并在每一步对列表的两半进行排序。不过,这可能不适合像这样的学习示例。

类似:

(define (mergesort l)
  (let sort-loop ((l l) (len (length l)))
    (cond
      ((null? l) '())
      ((null? (cdr l)) l)
      (else (merge (sort-loop (take (/ len 2) l) (/ len 2)))
                   (sort-loop (drop (/ len 2) l) (/ len 2)))))))))
(define (take n l)
  (if (= n 0)
      '()
      (cons (car l) (take (sub1 n) (cdr l)))))
(define (drop n l)
  (if (= n 0)
      l
      (drop (sub1 n) (cdr l))))

【讨论】:

  • 长度函数不需要遍历整个列表吗?这可能非常昂贵,因为必须为每个递归调用合并排序完成。我会在不使用长度的情况下定义拆分函数。它变得有点复杂,但它应该更有效。不过还没有测试过。
  • mergesort 不是递归的。 sort-loop 是。这正是我在mergeosrt 开头调用一次length 的原因。
  • 好的,我正在学习Scheme,所以我没有正确阅读代码。我认为每次调用排序循环时都会调用长度。相反,它是否被调用一次并绑定到变量lensort-loop ((l l) (len (length l))) 中的变量 l 会发生什么变化?新变量 l 是否绑定到外部 l?
  • 是的,在后续的迭代中,其他的东西都绑定到了内部的l,比如(take (/ len 2) l)的结果。 Let-loops 是(letrec ((sort-loop (lambda (l len) ...))) (sort-loop l (length l))) 的语法糖。例如,这就是您在 Caml 中编写循环的方式。
【解决方案2】:

一般来说,mergesort 做了很多列表操作,所以最好通过“就地”排序子部分来破坏性地做事情。您可以看到implementation of sort in PLT Scheme 的一个公共代码示例,它起源于 SLIB。 (乍一看可能看起来很吓人,但如果您阅读 cmets 并忽略旋钮和优化,您会在那里看到所有内容。)

此外,您应该查看SRFI-32 以了解有关排序接口的扩展讨论。

【讨论】:

  • 您发布的第一个链接似乎已经消失。你知道PLT Scheme中排序的实现有没有新的位置吗?
  • @Giorgio:从那以后我们切换到了 git,即where you can find this file now;但我们也将sort 的实现切换为不同的东西。参考代码见。
猜你喜欢
  • 2018-10-13
  • 1970-01-01
  • 2013-07-12
  • 2011-06-21
  • 1970-01-01
  • 1970-01-01
  • 2012-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多