【问题标题】:Endless adding to list for merge sort in lisp无休止地添加到列表中以在 lisp 中进行合并排序
【发布时间】:2016-06-23 09:40:32
【问题描述】:

这就是我在 lisp 中运行合并排序程序时发生的情况。

(MSORT'(1 2 3 4 5 6 7 8 9))

“堆栈溢出(深)”。

我很确定我经常将我的函数添加到列表中,但我不知道如何修复它。

(defun fhl(l) 
    (progn
            (setf msize (mod (length l) 2));; mod for size even check
            (setf size (- (/ (length l) 2) 1));;size of list -1 because we start on pos 1 in list
            (setf f (list (car l)));;size of list -1 because we start on pos 1 in list

            (if (eq msize 1) (setf size (+(floor size)1)));;if mod = 1 list is odd - .5 to round to whole number + 1 for more even spread
            (dotimes (i size) 
                    (setf f (append f (list (nth (+ i 1) l)))));; add next element in list size times
     f));;return new list


(defun shl(l)
    (progn
            (setf msize (mod (length l) 2));; mod for size even check
            (setf size  (/ (length l) 2));;size of list -1 because we start on pos 1 in list
            (if (eq msize 1) (setf size (+(floor size) 1)));;if mod = 1 list is odd - .5 to round to whole number + 1 for more even spread

            (dotimes (i size) ;; loop for i items 
                    (setf l (cdr l)))
     l))


(defun msort(l) 
            (if (null l)
                    '();;empty list
            )
            (if (= (length l) 1) l);;1 item
                    (progn
                            (setf f (fhl l))
                            (setf s (shl l))
                            (merge 'list (msort f) (msort s) #'<)

                    )

)

【问题讨论】:

  • 一些风格的 cmets 来改进你的代码。使用 let 而不是 progn 与 setf。 msort 中的第一个 if 语句实际上什么都不做。尝试在 shl 中使用 nthcdr 而不是 dotimes & setf/cdr。
  • 您是否使用过跟踪来查看您的函数被调用的频率以及使用哪些参数?
  • 您需要绑定变量,而不仅仅是分配给它们,这是未定义的行为,并且在任何递归函数中几乎肯定是灾难性的错误。不幸的是,Python 及其同类已经导致绑定和赋值在人们的脑海中变得混乱。
  • 空列表的if 不会取消递归。

标签: algorithm sorting recursion lisp mergesort


【解决方案1】:

您的MSORT-function 具有三种主体形式:

(defun msort (l)
  (if ...)
  (if ...)
  (progn ...))

由于函数从最后一个形式返回值,所以两个IFs 实际上并没有做任何事情。它们的值会被丢弃,并且始终返回来自PROGN 的值。

要解决此问题,您应该使用COND。正如人们在 cmets 中提到的,您还应该使用LET 来定义局部变量。

(defun msort (l)
  (cond ((null l) '())
        ((= (length l) 1) l)
        (t (let ((f (fhl l))
                 (s (shl l)))
             (merge 'list (msort f) (msort s) #'<)))))

另外,我必须说函数FHLSHL 的名字非常可怕。阅读代码的人应该能够理解函数的用途,而无需阅读其代码。我假设它们分别是“前半部分列表”和“后半部分列表”的首字母缩写词,因此您应该只使用这些名称。在 Common Lisp 中,通常首选使用完整的单词作为名称(这也适用于变量)。

使用两个函数来拆分列表也很糟糕,因为这两个函数依赖于另一个函数关于如何处理奇数列表长度的实现细节。最好将它们组合成一个函数。一个简单的实现是只使用一个LOOP

(defun split-list (list)
  "Split LIST into two halves. Returns a cons containing the halves."
  (loop with mid-point = (floor (length list) 2)
        for element in list
        for i from 0
        if (< i mid-point) collect element into first-half
          else collect element into second-half
        finally (return (cons first-half
                              second-half))))

这将返回一个 cons-cell 中的一半。然后您可以使用DESTRUCTURING-BIND 将它们绑定到MSORT 中的变量中。您也可以使用 VALUES 将它们作为多个值返回,并使用 MULTIPLE-VALUE-BIND 绑定它们,但我认为 cons-cell(或列表)更适合这种情况,因为不太可能有人只想要其中一个一半。

(defun mergesort (list)
  "Sort LIST using Mergesort-algorithm."
  (cond ((null list) '())
        ((= (length list) 1) list)
        (t (destructuring-bind (first-half . second-half) (split-list list)
             (merge 'list
                    (mergesort first-half)
                    (mergesort second-half)
                    #'<)))))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    • 2015-08-12
    • 1970-01-01
    相关资源
    最近更新 更多