我试图以最直观的方式在Common Lisp中编写一个简单的冒泡排序(也适用于Emacs Lisp):
(defun bubble-sort-long (list)
(labels ((rec (list acc)
(if (null list) (nreverse acc)
(progn
(dolist (x (cdr list))
(if (< x (car list))
(rotatef x (car list))))
(rec (cdr list) (push (car list) acc))))))
(rec list nil)))
这不起作用。因为 rotatef 只交换临时变量x和(汽车列表)的值,而不是列表中的比较元素。跟踪结果如下:
>(setf a '(1 3 2 5 4))
>(trace bubble-sort)
>(bubble-sort a)
0: (BUBBLE-SORT-LONG (1 3 2 5 4))
0: BUBBLE-SORT-LONG returned (1 2 2 4 4)
我想最直接的解决方法是定义一个“破坏性”的dolist宏,它将x直接指向分配给列表中的迭代元素。有点像ndolist,所以我可以做以下工作:
(setf a '(1 2 3))
(ndolist (x a)
(setf x (+ 1 x)))
会产生:(2 3 4)。
此外,如果您可以使用lisp提供有关气泡排序的更直观的想法,请同时提示。
在ruby中,类似的算法类似于:
class Array
def bubble_sort
self.each_with_index do |a,j|
i=j+1
while i<self.length
if (self[j]>self[i])
self[j],self[i]=self[i],self[j]
end
i=i+1
end
end
end
有趣的是我仍然需要使用“并行分配”来交换值。 Ruby不支持(鼓励)使用临时变量通过C / C ++风格的引用进行交换。
答案 0 :(得分:3)
这有一些问题。这也不是那么容易。
我会给你一些提示:
变量指向一个值,而不是一个地方。修改变量永远不会改变另一个地方或另一个变量。
您可以更换列表的第一项和第二项,例如(rotatef (first list) (second list))
永远不要更改代码中的文字列表 - 这是一个常量。如果破坏性地更改列表,请使用consed列表(例如,由COPY-LIST或LIST或......创建)。
我们可以想到两个版本的BUBBLE-SORT:一个是破坏性的,另一个不是。
我还会用循环替换tail-recursive函数。在Scheme中你将使用尾递归函数,在Common Lisp中大多数都没有。
答案 1 :(得分:3)
关于你的原始问题:所以你想要一个dolist
变体,它不仅将迭代变量绑定到提供的名称,而是使它成为一个位置。应该可以通过将该宏扩展为symbol-macrolet
上的循环来实现,这将为每个迭代步骤将名称扩展为不同的位置形式。
关于冒泡排序的实现:冒泡排序反正效率非常低,因此尾部递归的麻烦是浪费精力。如果你完全关心效率,你可以选择一个更好的算法,冒泡排序只是为了演示目的,所以你应该尽可能地坚持伪代码实现。这是一个执行该操作的lisp实现:
(defun bubble-sort (seq)
(loop for n from (length seq) downto 2 and swapped = nil
do (dotimes (i (1- n))
(when (> (elt seq i) (elt seq (1+ i)))
(rotatef (elt seq i) (elt seq (1+ i)))
(setf swapped t)))
while swapped))
此实现的另一个好处是它使用序列协议,因此它可以使用列表和向量(一维数组)。
编辑:正如评论中所讨论的,在列表上使用序列协议的随机访问操作是非常低效的,所以这里有一个避免它们的版本(它的缺点是它不再适用于向量):(defun list-bubble-sort (list)
(when list
(loop with result = ()
for swapped = nil
do (let ((x (car list)))
(setf list (loop for y in (cdr list)
when (> x y) collect y and do (setf swapped t)
else collect x and do (setf x y)))
(push x result))
unless (and list swapped) return (append list result))))
此版本返回已排序的列表,不会改变原始列表。
答案 2 :(得分:0)
最后我发现了这个:
(defmacro dolist* ((iterator list &optional return-value) &body body)
"Like DOLIST but destructuring-binds the elements of LIST.
If ITERATOR is a symbol then dolist* is just like dolist EXCEPT
that it creates a fresh binding."
(if (listp iterator)
(let ((i (gensym "DOLIST*-I-")))
`(dolist (,i ,list ,return-value)
(destructuring-bind ,iterator ,i
,@body)))
`(dolist (,iterator ,list ,return-value)
(let ((,iterator ,iterator))
,@body))))