冒泡排序常见的Lisp错误

时间:2016-06-23 02:15:31

标签: sorting common-lisp bubble-sort

我正在尝试在Common Lisp中实现冒泡排序,并且我很难掌握我的方向。[见下文]是我到目前为止所得到的,据我所知,它遵循算法,但我收到错误“用参数调用未定义函数SORTED()。”当我运行它。我似乎无法找到原因。

(defun bubble (lis)
  (let ((sorted nil) (j 0))
    (do () ((not sorted))
      (progn 
        (setf sorted t)
        (do (i j (+ i 1))
            (if (< (nth i lis) (nth (+ i 1) lis))
                (progn
                  (swap1 (lis (nth i lis) (nth (+ i 1) lis)))
                  (setf sorted nil)
                  )
              )
          )
        )
      )
    )
  )

3 个答案:

答案 0 :(得分:5)

每次调用NTH都需要遍历列表。如果将列表视为向量,则可能应该使用向量。如果你真的不关心效率,你可能仍然希望使用ELT而不是NTH,因为ELT适用于任何类型的序列。这样,您可以传递矢量或列表,并且至少其中一个将合理地工作(只要冒泡排序可以有效)。 你可能最终会得到像Rosetta Code那样的东西。

顺便说一句,Rosetta Code有一个列表迭代冒泡排序的例子,所以我不会复制它。相反,下面是一个递归版本,我改编自Prolog(罗马巴塔克)。因此,它不一定更好,但它使用多个值,ETYPECASEDESTRUCTURING-BIND,...显然通常不会教授的功能。

(defun bubble-sort (list)
  (labels
      ((bsort (list acc)
         (etypecase list
           (null acc)
           (cons (destructuring-bind (head . tail) list
                   (multiple-value-bind (new-tail max)
                       (bubble head tail)
                     (bsort new-tail
                            (cons max acc)))))))
       (bubble (x list)
         (etypecase list
           (null (values nil x))
           (cons (destructuring-bind (y . tail) list
                   (multiple-value-bind (new-tail max)
                       (bubble (max x y) tail)
                     (values (cons (min x y) new-tail)
                             max)))))))
    (bsort list nil)))

答案 1 :(得分:4)

我们可以做很多事情来改进这段代码。

您可以采取一些措施来改善您的问题。如果您再问一次,请提供测试用例和具体问题。

  1. 缩进

    Lisp语法相对较少,但我们使用缩进来帮助突出显示代码的结构。大多数支持Lisp的编辑都有助于管理它。与传统缩进方法最明显的不同之处是关闭以下几行括号。我缩进了mergelist函数来显示一个更易读的函数体 - 好吧,至少对我而言。

    (defun bubble (lis)
      (let ((sorted nil) (j 0))
        (do () ((not sorted))
          (progn 
            (setf sorted t)
            (do (i j (+ i 1))
                (if (< (nth i lis) (nth (+ i 1) lis))
                    (progn
                      (swap1 (lis (nth i lis) (nth (+ i 1) lis)))
                      (setf sorted nil))))))))
    
  2. Loop vs DO

  3. DO在lisp中有很长的血统,但说实话,我总是在DO中犯错,所以不要经常使用它。我永远不会记得返回表格的位置,增量。我倾向于使用LOOP

    但首先,我们不需要使用预测。大多数循环结构对它们迭代的代码都有隐含的预测,所以

    (defun bubble-1 (lis)
      (let ((sorted nil) (j 0))
        (do () ((not sorted))
    
          (setf sorted t)
          (do (i j (+ i 1))
          (if (< (nth i lis) (nth (+ i 1) lis))
              (swap1 (lis (nth i lis) (nth (+ i 1) lis)))
              (setf sorted nil))))))
    

    稍微好一些。查看你的代码,调用swap1,它必须是某个地方提供的defun。这行也有语法问题,因为'lis'显示为函数调用。

    让我们尝试评估一下这个功能,看看会发生什么

    ; in: DEFUN BUBBLE-1
    ;     (LET ((SORTED NIL) (J 0))
    ;       (DO ()
    ;           ((NOT SORTED))
    ;         (SETF SORTED T)
    ;         (DO (I
    ;              J
    ;              (+ I 1))
    ;             (IF (< # #) (SWAP1 #) (SETF #)))))
    ; 
    ; caught STYLE-WARNING:
    ;   The variable J is defined but never used.
    
    ; in: DEFUN BUBBLE-1
    ;     (DO (I
    ;          J
    ;          (+ I 1))
    ;         (IF
    ;          (< (NTH I LIS) (NTH (+ I 1) LIS))
    ;          (SWAP1 (LIS (NTH I LIS) (NTH # LIS)))
    ;          (SETF SORTED NIL)))
    ; --> BLOCK 
    ; ==>
    ;   (LET (I J (+ I))
    ;     (TAGBODY
    ;       (GO #:G3)
    ;      #:G2
    ;       (TAGBODY)
    ;       (PSETQ + 1)
    ;      #:G3
    ;       (UNLESS IF (GO #:G2))
    ;       (RETURN-FROM NIL (PROGN (< # #) (SWAP1 #) (SETF #)))))
    ; 
    ; caught WARNING:
    ;   undefined variable: I
    
    ; --> BLOCK LET TAGBODY UNLESS 
    ; ==>
    ;   (IF IF
    ;       NIL
    ;       (GO #:G2))
    ; 
    ; caught WARNING:
    ;   undefined variable: IF
    
    ;     (LIS (NTH I LIS) (NTH (+ I 1) LIS))
    ; 
    ; caught STYLE-WARNING:
    ;   undefined function: LIS
    
    ;     (SWAP1 (LIS (NTH I LIS) (NTH (+ I 1) LIS)))
    ; 
    ; caught STYLE-WARNING:
    ;   undefined function: SWAP1
    ; 
    ; compilation unit finished
    ;   Undefined functions:
    ;     LIS SWAP1
    ;   Undefined variables:
    ;     I IF
    ;   caught 2 WARNING conditions
    ;   caught 3 STYLE-WARNING conditions`enter code here`
    

    哇。这告诉我们一些事情

    1. 不使用嵌套DO中的变量J.删除它。
    2. 嵌套循环中DO的语法错误。它需要是一般形式

      (DO ((var init step))
          (termination-test result-form)
        statement)
      
    3. 嵌套的do缺少终止测试。 i的变量声明也缺少初始化。

      1. Let有点多余,你可以将sort的声明移动到do

        (do ((sorted nil)) ((not sorted ) ... )
        
      2. 表格

        (SWAP1 (LIS (NTH I LIS) (NTH (+ I 1) LIS)))
        
      3. 有两个问题。首先,SWAP1未定义。其次,形式(LIS(NTH I LIS)(NTH(+ I 1)LIS))可能不正确,因为LIS出现在函数调用位置。出现在表单前面的任何内容都必须是一个函数。在这个cas LIS中是一个参数。

        幸运的是,Common Lisp有一个内置函数,可以为我们交换值 - 它叫做rotatef。因此,entires形式需要看起来像

        (rotatef (nth I lis) (nth (1+ i) lis))
        
        1. 一旦函数运行,它在do中没有结果形式,因此排序的数组永远不会返回给调用者。你将看不到输出。你需要考虑这里嵌套循环的事实。
        2. 我会考虑一下你的算法。正如Zephyr Pellerin所说,递归解决方案会更好,所以除非你的任务是使用迭代解决方案

答案 2 :(得分:1)

你应该研究David Hodge的答案,详细说明代码的所有问题。在这里,我提供了使用do特殊形式的冒泡排序的迭代版本。与您尝试使用代码实现的算法的唯一主要区别是使用变量end,每次减少变量数量都会减少:

(defun bubble (lis)
  (let ((sorted nil)
        (end (length lis)))
    (do () (sorted lis)
      (setf sorted t)
      (decf end)
      (do ((i 0 (1+ i)))
          ((>= i end))
        (when (< (nth i lis) (nth (1+ i) lis))
          (rotatef (nth i lis) (nth (1+ i) lis))
          (setf sorted nil))))))