【问题标题】:LISP better understanding of multilevel listsLISP 更好地理解多级列表
【发布时间】:2018-10-28 17:35:19
【问题描述】:

我试图通过创建一个函数来更好地理解 lisp 中的多级列表,在该函数中,我从多级列表中删除所有不是数字的元素。我首先尝试使用 sriv0 函数将其展平,然后检查列表的第一个参数是否为数字。但是我在尝试该功能时遇到问题
(func '(5 (5 2 (8)(7 (9)5)))) .我得到 :COND :variable IF HAS NO VALUE

代码:

    (DEFUN striv0 (lis)
(COND        ((NULL lis) NIL)
    ((ATOM lis) (LIST lis))
    (T (APPEND (striv0 (FIRST lis))
            (striv0 (REST lis))))
))

(DEFUN func (lis) 
(LET( (newList (striv0 lis))  ))
(COND ((NULL newList) NIL)
    ( T(IF (NUMBERP (FIRST newList))   
             ((func(REST newList)))))))

我相信我已经设法解决了这个问题,方法是通过检查数字参数或内部列表来将第二个函数融入第一个函数,如果两者都不是,它会沿着列表进一步移动。:

    (DEFUN  checkNumber (lis)
(COND    ((NULL lis) NIL)
    ((ATOM lis) (LIST lis))
    (T (if ( or(NUMBERP ( FIRST lis))(LISTP (FIRST lis)))
         (APPEND ( checkNumber (FIRST lis))
            ( checkNumber (REST lis)))
       ( checkNumber (REST lis))  )
     )   
)
)

【问题讨论】:

  • 第二个函数中的COND 语法错误。 (cond ... (if ...)) 应该是 (cond ... (t (if ...))) 就像上面的其他函数一样。此外,您正在调用STRIV0,但您没有对返回值做任何事情,因此它实际上并没有完成任何事情(STRIV0 没有修改参数,它返回一个新列表)。跨度>
  • 我已经修改了代码。但是现在我得到了 "program stack overflow.RESET" 。我跟踪了这​​个函数,我可以看到在展平列表之后它没有做任何事情(除了它在崩溃之前写入 func'(5 5 2 8 7 9 5) 多达数千个。
  • 还有一些问题。首先,您应该使用LET 来定义局部变量,而不仅仅是SETF 定义一个不存在的变量。堆栈溢出是由一次又一次地用整个列表调用FUNC 引起的。您应该改为在列表的REST 上调用它。此外,(append (first newList) ...) 不起作用,因为 NEWLISTFIRST 是一个数字,但 APPEND 需要列表。您应该改用CONS。对(append nil ...) 的调用也是不必要的,因为将列表附加到NIL(一个空列表)总是会产生相同的列表。
  • 我已经修改了,但是在 lisp 中是否接受离开没有 else 子句的 IF 语句?另外,当我在 REST newList 上应用 func 时,它应该重做所有步骤,这是否意味着在进行展平时它会因为没有嵌套列表而溢出?
  • 以可读的方式格式化程序代码很重要。不幸的是,您发布的内容无法阅读。尝试改进缩进和格式。这与任何其他编程语言没有什么不同——因为 Lisp 是作为嵌套列表编写的,所以缩进和格式化程序相对容易。您需要查看代码中列表的开始位置和结束位置。

标签: lisp common-lisp


【解决方案1】:

这里有一些逐行反馈和一些提示:

(DEFUN func (lis)                    ; why is this called FUNC
                                     ;   and not something useful?
                                     ; why is the variable called
                                     ;  LIS and not LIST?

(LET( (newList (striv0 lis))  ))     ; why is this not indented?
                                     ; why does LET not have
                                     ;   body forms?
                                     ; why is the LET ending here?


(COND ((NULL newList) NIL)           ; why is this not indented?
    ( T(IF (NUMBERP (FIRST newList)) ; why is this not indented?
             ((func(REST newList)))))))  ; why are there so
                                         ;   many parentheses?
                                         ; why is it starting
                                         ;   with two parentheses?

【讨论】:

    【解决方案2】:

    首先,格式化您的代码。公认的约定是使用小写符号并在符号名称中用破折号分隔单词部分,例如。 G。 new-list。缩进用于在同一文本列中对齐同一级别的表单。 Lisp 风格的文章有很多种,here 就是其中之一。以下是您的代码的外观:

    (defun striv0 (lis)
      (cond ((null lis) nil)
            ((atom lis) (list lis))
            (t (append (striv0 (first lis))
                       (striv0 (rest lis))))))
    
    (defun func (lis) 
      (let ((new-list (striv0 lis))))
      (cond ((null new-list) nil)
            (t (if (numberp (first new-list))
                   (cons (first new-list) (func (rest new-list)))))))
    

    一个好的编辑器会为你做缩进。如果缩进不符合您的期望,您就知道您犯了语法错误。编辑器还将显示匹配的括号对。

    Let 将绑定列表作为第一个参数,然后在其body 中使用任意数量的表单。只有在该体内,绑定才有效。您想将 cond 表单放入该正文中:

    (defun func (lis) 
      (let ((new-list (striv0 lis)))
        (cond ((null new-list) nil)
              (t (if (numberp (first new-list))
                     ((func (rest new-list))))))))
    

    注意缩进是如何自动反映代码结构的:​​cond 表单现在缩进了两个空格,以表明它在 let 正文内。我实际编辑的是将let 表单的右括号移到末尾。

    最后一个表单 ((func (rest new-list))) 不是一个有效的 Lisp 表单。表单必须始终以运算符开头(可以是符号或 lambda 形式)。我认为您的问题在早期版本中看起来正确:(cons (first new-list) (func (rest new-list)))

    我现在尝试为这些函数找到更好的名称。您的striv0 可能通常被命名为flatten。 “多级列表”在 Lisp 中通常称为“树”。您的func 可能因此被称为keep-numbers-from-tree

    我不确定扁平化是否是您要求的一部分。无论如何,我都会将其分开,因为每次通过递归再次执行此操作似乎是多余的。先展平,然后过滤数字。

    如果展平不是您的要求的一部分,请不要这样做。遍历树时,每个点只有三种可能性:它是一个列表(然后递归),它是一个数字(保持),或者它是一个非数字原子(跳过)。

    【讨论】:

    • 你推荐使用什么编辑器来实现 lisp?通常我使用记事本用 lisp 或其他语言(或 Visual Studio,但仅适用于 C++)编写
    • 我通常在紧要​​关头使用 Emacs 或 Vim,但学习这通常是一项额外的任务,可能会分散学习 Lisp 的目标(尽管 Emacs 主要是在其内部构建、扩展和配置的)自己的 Lisp 方言)。我认为 Atom 或 Sublime 可能会为 Windows 用户提供更熟悉的环境,尽管我不确定 Lisp 的插件情况。如果你想走 Emacs 路线,看看 Portacle,它以便携的方式捆绑了你需要的一切。
    猜你喜欢
    • 1970-01-01
    • 2012-07-29
    • 1970-01-01
    • 2016-04-09
    • 2018-11-04
    • 1970-01-01
    • 1970-01-01
    • 2021-07-03
    • 1970-01-01
    相关资源
    最近更新 更多