【问题标题】:Bubble Sort Common Lisp Error冒泡排序常见的 Lisp 错误
【发布时间】:2016-10-25 04:11:11
【问题描述】:

我正在尝试在 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)
                  )
              )
          )
        )
      )
    )
  )

【问题讨论】:

  • 请复制粘贴代码而不是链接图像。
  • 我很抱歉,@MillieSmith。
  • 我想回答这个问题,但我陷入了太多的事情。我认为这是家庭作业,所以我会给出粗略的说明:不要在一堆换行符上分解你的结束括号,你的一些声明拼写错误,最好使用 loop 来表示你想要突破的语句在某个确定的点。最后但同样重要的是,像这样的事情理想地递归地完成,冒泡排序是理想的。
  • 总而言之:我认为您可能在 Scheme 中有某种背景,因为变量名称 LIS,但 list 是完全有效的(而且,我认为,口语) 变量名。函数、变量、类、包等在 Common Lisp 中有自己的命名空间。

标签: sorting common-lisp bubble-sort


【解决方案1】:

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

顺便说一下,R​​osetta Code 有一个用于列表的迭代冒泡排序的示例,所以我不会复制它。相反,下面是我改编自 Prolog 的递归版本(由 Roman Barták 编写)。因此,它不一定更好,但它使用多个值,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)))

【讨论】:

    【解决方案2】:

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

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

    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. 循环与 DO

    DO 在 lisp 中有着悠久的血统,但说实话,我总是用 DO 出错,所以不要经常使用它。我永远不记得返回表格的去向,增量。我倾向于使用循环

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

    (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)
      

    嵌套的 do 缺少终止测试。 i 的变量声明也缺少初始化。

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

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

      (SWAP1 (LIS (NTH I LIS) (NTH (+ I 1) LIS)))
      

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

    幸运的是,Common Lisp 有一个内置函数,可以为我们交换值 - 它称为 rotatef。所以整个表单需要看起来像

    (rotatef (nth I lis) (nth (1+ i) lis))
    
    1. 一旦函数运行,它在do中没有结果形式,所以排序后的数组永远不会返回给调用者。您将看不到任何输出。您需要考虑一下您在此处嵌套循环的事实。

    我会考虑一下您的算法。正如 Zephyr Pellerin 上面所说,递归解决方案会更好,所以除非您的任务是使用迭代解决方案

    【讨论】:

      【解决方案3】:

      您应该研究 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)))))) 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-01-19
        • 2018-11-13
        • 2023-04-07
        • 2018-05-04
        • 2014-06-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多