【问题标题】:LISP - get last list of a listLISP - 获取列表的最后一个列表
【发布时间】:2013-12-02 06:17:18
【问题描述】:

我试图弄清楚如何从另一个列表中获取 last(非空)列表,或者如果没有这样的列表(递归),则返回 nil。这是一个家庭作业,因此我正在寻找有关该方法的帮助,而不一定是它的代码。示例:

(lastele '(1 (2 3) 4 5))  ;=> (2 3)
(lastele '(1 (2 3) (4 5)) ;=> (4 5)
(lastele '(1 2 3 4 5))    ;=> NIL

我试图遍历列表,如果遇到子列表,我会检查列表的其余部分是否包含更多非空子列表,如果有,继续将列表设置为那个,并重复,直到我们有一个空列表。

(defun lastele2 (L)
  (if (null L)
    '()
    (if (hasMoreLists (rest L))
      (lastele2 (rest L))
      (first L))))

不过,我似乎无法让hasMoreLists 工作。在内部返回 tf 只是错误。这是解决此问题的最佳方法吗?

【问题讨论】:

  • 如何更好地缩进你的代码?
  • 我对这个有趣的家庭作业有了答案,并准备点击Send。但在我能做到之前,狗把我输入的所有东西都吃光了。 ;-)
  • @RainerJoswig 当我显然是 LISP 新手并寻求帮助时,不要这么粗鲁如何?
  • @Drew 是的,这是一个分配问题,但这就是为什么我在方法上寻求帮助,而不一定是代码本身。我不喜欢被勺子喂的代码,那是没有办法学习的。
  • 要求缩进代码有什么“粗鲁”的?为什么这里的每个人都应该查看该代码并且必须导出它并缩进它。缩进对于使 Lisp 代码可读性非常重要。您可以节省我们的工作并投入更多精力来写下您的问题。如果要发布源代码,请确保它是可读的。你仍然可以做到。您可以编辑您的问题并正确缩进源代码。

标签: list recursion lisp common-lisp


【解决方案1】:

不,这不是解决此问题的最佳方法。要查找列表的其余部分是否有更多列表,您需要搜索它 - 如果有,则重新开始扫描列表的其余部分。

即你来来回回做了很多事情。

取而代之的是just search along,并更新一个边变量以指向您在此过程中找到的任何列表。

(defun lastele (lst &aux a)          ; a is NIL initially
  (dolist (e lst a)                  ; return a in the end
    (if (consp e) (setq a e))))

【讨论】:

  • the documentation&aux真的很有用。
  • @Sylwester Common Lisp 是一种实用语言。
【解决方案2】:

首先,请注意,您隐含地假设所有子列表都不是空列表;如果它们可能是空列表,那么 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-listresult 作为可选参数来使last-listll 具有相同的功能:

(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列表的第一个元素
结束如果
listlist 的其余部分
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 iflist 的第一个元素是一个列表)
返回 列表的第一个元素
其他
返回零
如果结束
如果结束

【讨论】:

  • 我知道内置函数,但是我需要编写它来理解它背后的方法(也是功课)
  • 现在只要我们被允许使用标签 =(
  • @Nexion question 中没有提到这是一项任务,也没有提到您知道内置函数。有了这个澄清,我不介意展示和讨论递归解决方案,但请注意 StackOverflow 是为“专业和狂热的程序员”准备的。这当然包括学生,但重要的是要在问题中指出问题的所有要求。
  • @Nexion 你能用PROGGO 吗? :)
  • @JoshuaTaylor 我将指出未来有关作业的问题,抱歉造成混淆。将编辑 OP 以反映以后查找类似问题的人的情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-14
  • 2010-10-30
  • 2017-02-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多