我已经完成了Graham Common Lisp第5章练习5,该练习需要一个函数,该函数接受对象X和向量V,并返回V中紧接X的所有对象的列表。
它的工作原理如下:
> (preceders #\a "abracadabra")
(#\c #\d #r)
我已经完成了递归版本:(defun preceders (obj vec &optional (result nil) &key (startt 0))
(let ((l (length vec)))
(cond ((null (position obj vec :start startt :end l)) result)
((= (position obj vec :start startt :end l) 0)
(preceders obj vec result
:startt (1+ (position obj vec :start startt :end l))))
((> (position obj vec :start startt :end l) 0)
(cons (elt vec (1- (position obj vec :start startt :end l)))
(preceders obj vec result
:startt (1+ (position obj vec
:start startt
:end l))))))))
它可以正常工作,但是我的老师给了我以下批评:“这会反复调用length。对于向量来说并不是很糟糕,但是仍然没有必要。(对于用户而言)更有效,更灵活的代码是像其他序列处理函数一样定义它。使用:start和:end关键字参数,其他方法序列函数具有相同的默认初始值。应该最多调用一次长度。”
我正在咨询Common Lisp教科书和google,但在这方面似乎没有什么帮助:我不知道他“使用:start和:end关键字参数”是什么意思,而且我不知道如何“通话长度仅一次”。如果您能给我一些想法,以改进我的代码以满足我的老师发布的要求,我将不胜感激。
更新:
现在我想出了以下代码:
(defun preceders (obj vec
&optional (result nil)
&key (start 0) (end (length vec)) (test #'eql))
(let ((pos (position obj vec :start start :end end :test test)))
(cond ((null pos) result)
((zerop pos) (preceders obj vec result
:start (1+ pos) :end end :test test))
(t (preceders obj vec (cons (elt vec (1- pos)) result)
:start (1+ pos) :end end :test test)))))
我得到这个批评:“当您有一个复杂的递归调用,并且在多个分支中重复执行时,通常更容易先执行该调用,将其保存在本地变量中,然后在更简单的IF或COND中使用该变量。”
另外,对于该函数的迭代版本:
(defun preceders (obj vec)
(do ((i 0 (1+ i))
(r nil (if (and (eql (aref vec i) obj)
(> i 0))
(cons (aref vec (1- i)) r)
r)))
((eql i (length vec)) (reverse r))))
我受到批评“在一个更好的点开始DO,并删除重复的> 0测试”
最佳答案
此类函数的典型参数列表为:
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
...
)
如您所见,它具有START和END参数。
TEST是默认的比较功能。使用(funcall测试项目(区域向量i))。
通常还有一个KEY参数...
对于PRECEDERS的每个递归调用,都会重复调用LENGTH。
我将执行非递归版本,并在向量上移动两个索引:一个用于第一项,一个用于下一项。每当下一个项目是要查找的项目的EQL时,就将第一个项目推到结果列表中(如果它不是该项目的成员)。
对于递归版本,我将编写一个由PRECEDERS调用的第二个函数,该函数接受两个以0和1开头的索引变量,并使用它。我不会调用POSITION。通常,此函数是通过PRECEDERS内部的LABELS进行的局部函数,但是为了使编写起来更容易一点,辅助函数也可以位于外部。
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
(preceders-aux item vector start end test start (1+ start) nil))
(defun preceders-aux (item vector start end test pos0 pos1 result)
(if (>= pos1 end)
result
...
))
有帮助吗?
这是使用LOOP的迭代版本:
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
(let ((result nil))
(loop for i from (1+ start) below end
when (funcall test item (aref vector i))
do (pushnew (aref vector (1- i)) result))
(nreverse result)))