首先,请注意,您隐含地假设所有子列表都不是空列表;如果它们可能是空列表,那么 nil 是一个模棱两可的结果,因为您无法判断您的函数返回 nil 是因为没有子列表,还是因为有子列表,而最后一个是空的。例如,
(fn '(1 2 3 4 5)) ;=> nil because there are no sublists
(fn '(1 2 3 () 5)) ;=> nil because there are sublists, and the last one is nil
所以,假设顶层列表中没有非空子列表,我们可以继续。
使用标准函数的非家庭作业解决方案
你不需要写这个。您可以将find-if 与谓词listp 一起使用,并使用关键字参数:from-end t 指定要从末尾开始搜索:
CL-USER> (find-if 'listp '(1 (2 3) 4 5) :from-end t)
(2 3)
CL-USER> (find-if 'listp '(1 (2 3) (4 5)) :from-end t)
(4 5)
CL-USER> (find-if 'listp '(1 2 3 4 5) :from-end t)
NIL
自己写
如果你需要写这样的东西,你最好的办法是使用一个递归函数来搜索一个列表并跟踪你看到的最新列表元素作为结果(起始值是@987654330 @) 并且当您最终到达列表末尾时,您将返回该结果。例如,
(defun last-list (list)
(labels ((ll (list result) ; ll takes a list and a "current result"
(if (endp list) ; if list is empty
result ; then return the result
(ll (cdr list) ; else continue on the rest of list
(if (listp (car list)) ; but with a "current result" that is
(car list) ; (car list) [if it's a list]
result))))) ; and the same result if it's not
(ll list nil))) ; start with list and nil
这里的局部函数 ll 是尾递归的,一些实现会将其优化为循环,但使用真正的循环构造会更习惯。例如,do,你会写:
(defun last-list (list)
(do ((result nil (if (listp (car list)) (car list) result))
(list list (cdr list)))
((endp list) result)))
如果不想使用标签,可以将其定义为两个函数:
(defun ll (list result)
(if (endp list)
result
(ll (cdr list)
(if (listp (car list))
(car list)
result))))
(defun last-list (list)
(ll list nil))
或者,您可以通过让last-list 将result 作为可选参数来使last-list 和ll 具有相同的功能:
(defun last-list (list &optional result)
(if (endp list)
result
(last-list (cdr list)
(if (listp (car list))
(car list)
result))))
在所有这些情况下,您实现的算法本质上都是迭代的。这是
输入: 列表
结果 ← nil
while(列表不为空)
如果(list的第一个元素是一个列表)
result ← 列表的第一个元素
结束如果
list ← list 的其余部分
end while
return result
基于问题中的代码的东西
不过,我们仍然可以找到更接近原始方法的方法(这将使用更多堆栈空间)。首先,您的原始代码带有适当的缩进(和一些换行符,但那里的编码风格更灵活):
(defun lastele2 (L)
(if (null L)
'()
(if (hasMoreLists (rest L))
(lastele2 (rest L))
(first L))))
看起来您尝试使用的方法是将列表 L 的最后一个子列表定义为:
-
nil,如果L为空;
- 如果
(rest L) 有一些子列表,不管(rest L) 的最后一个子列表是什么;和
- 如果
(rest L) 没有一些子列表,那么(first L)。
不过,最后一行不太正确。应该是
- 如果
(rest L) 没有一些子列表,那么(first L) 如果(first L) 是一个列表,否则nil。
现在,您已经有办法检查(rest L) 是否有 任何(非空)子列表;您只需检查(lastele2 (rest L)) 是否返回您nil。如果它返回nil,那么它不包含任何(非空)子列表。否则它返回列表之一。这意味着你可以写:
(defun last-list (list)
(if (endp list) ; if list is empty
nil ; then return nil
(let ((result (last-list (rest list)))) ; otherwise, see what (last-list (rest list)) returns
(if (not (null result)) ; if it's not null, then there were more sublists, and
result ; last-list returned the result that you wantso return it
(if (listp (first list)) ; otherwise, if (first list) is a list
(first list) ; return it
nil))))) ; otherwise return nil
这是实现本质上的递归算法;返回子问题的值,然后 lastList 在检查结果后返回一个值:
函数: lastList(list)
如果(list为空)
返回零
否则
结果 ← lastList(list)
如果(result不是nil)
返回 结果
else if(list 的第一个元素是一个列表)
返回 列表的第一个元素
其他
返回零
如果结束
如果结束