【发布时间】:2010-12-21 19:09:16
【问题描述】:
我已经完成了 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))))))))
它工作正常,但我的老师给了我以下批评:
“这会重复调用长度。对于向量来说还不错,但仍然没有必要。更有效和更灵活(对用户而言)的代码是像其他序列处理函数一样定义它。使用 :start 和 :end 关键字参数,与其他序列函数一样,具有相同的默认初始值。length 最多需要调用一次。"
我正在查阅 Common Lisp 教科书和谷歌,但在这方面似乎没有什么帮助:我不知道他所说的“使用 :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 测试”
【问题讨论】:
标签: lisp common-lisp