我正在尝试在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)
)
)
)
)
)
)
)
答案 0 :(得分:5)
每次调用NTH
都需要遍历列表。如果将列表视为向量,则可能应该使用向量。如果你真的不关心效率,你可能仍然希望使用ELT
而不是NTH
,因为ELT
适用于任何类型的序列。这样,您可以传递矢量或列表,并且至少其中一个将合理地工作(只要冒泡排序可以有效)。
你可能最终会得到像Rosetta Code那样的东西。
顺便说一句,Rosetta Code有一个列表迭代冒泡排序的例子,所以我不会复制它。相反,下面是一个递归版本,我改编自Prolog(罗马巴塔克)。因此,它不一定更好,但它使用多个值,ETYPECASE
,DESTRUCTURING-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)
我们可以做很多事情来改进这段代码。
您可以采取一些措施来改善您的问题。如果您再问一次,请提供测试用例和具体问题。
缩进
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))))))))
Loop vs DO
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`
哇。这告诉我们一些事情
嵌套循环中DO的语法错误。它需要是一般形式
(DO ((var init step))
(termination-test result-form)
statement)
嵌套的do缺少终止测试。 i的变量声明也缺少初始化。
Let有点多余,你可以将sort的声明移动到do
(do ((sorted nil)) ((not sorted ) ... )
表格
(SWAP1 (LIS (NTH I LIS) (NTH (+ I 1) LIS)))
有两个问题。首先,SWAP1未定义。其次,形式(LIS(NTH I LIS)(NTH(+ I 1)LIS))可能不正确,因为LIS出现在函数调用位置。出现在表单前面的任何内容都必须是一个函数。在这个cas LIS中是一个参数。
幸运的是,Common Lisp有一个内置函数,可以为我们交换值 - 它叫做rotatef。因此,entires形式需要看起来像
(rotatef (nth I lis) (nth (1+ i) lis))
我会考虑一下你的算法。正如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))))))