gpt4 book ai didi

lisp - lisp 函数细化

转载 作者:太空宇宙 更新时间:2023-11-03 18:37:18 24 4
gpt4 key购买 nike

我已经完成了 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 关键字参数,其他序列函数的方式,具有相同的默认初始值。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测试”

最佳答案

此类函数的典型参数列表为:

(defun preceders (item vector 
&key (start 0) (end (length vector))
(test #'eql))
...
)

如您所见,它具有 START 和 END 参数。

TEST 是默认的比较函数。使用 (funcall test item (aref vector 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)))

关于lisp - lisp 函数细化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1822382/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com