带列表的Lisp递归

带列表的Lisp递归,lisp,common-lisp,Lisp,Common Lisp,我需要一个函数,它将接受一个包含单词的列表,并在任何时候找到单词“FOO”时将该列表拆分为两个列表。我已经想出了一个递归解决方案,可能不是最好的,但我有点麻烦。我只需要传递1个参数,列表将被分析,但我不知道如何建立第二个列表。有什么建议吗?谢谢 ;Splits a list into 2 if the word 'FOO' is present ;---------------------------------------------------------------------- ;LOA

我需要一个函数,它将接受一个包含单词的列表,并在任何时候找到单词“FOO”时将该列表拆分为两个列表。我已经想出了一个递归解决方案,可能不是最好的,但我有点麻烦。我只需要传递1个参数,列表将被分析,但我不知道如何建立第二个列表。有什么建议吗?谢谢

;Splits a list into 2 if the word 'FOO' is present
;----------------------------------------------------------------------
;LOAD FILE: (load "C:\\split.lisp")
;USAGE: (split '(with great power foo comes great responsibility) '())
;OUTPUT: ((with great power)(comes great responsibility))

(defun split (x y)
  (cond
    ( ;IF: first element in list is nil
      (EQ (car x) nil)
        x ;RETURN the list
    )
    ( ;ELSE IF: first element is 'FOO'
      (EQ (car x) 'FOO)
        (cons (reverse y ) (cons (cdr x) nil)) 
    )
    ( ;ELSE: recursively call split but pass the rest of x and 
      ;prepend y with the head of x
      t
        (split (cdr x) (cons (car x) y))
    )
  ) ;END cond
) ;END split

第一个测试应该是不同的

以下不是一个真正好的解决方案:它不是尾部递归,并且使用副作用。但还是

(defun split (x)
  (cond ((null x) x)
        ((eq (first x) 'foo)
         (list nil (rest x)))
        (t (let ((l (split (rest x))))
             (push (first x) (first l))
             l))))
上面使用了
PUSH
宏。CommonLisp的一个有趣功能是,您可以使用places进行修改。在这种情况下,我们修改要返回的列表的第一个子列表。我们将列表的第一个元素推送到第一个子列表上

CL-USER 12 > (split '(1 2 3 foo a b c))
((1 2 3) (A B C))
在公共Lisp中,通常以非递归方式编写解决方案

在递归版本中,将函数缩减为一个参数的典型方法是:使用一个参数编写函数,然后该函数使用两个参数调用辅助函数。还可以使用
标签
本地定义助手函数

(defun split (lst)
  (let* ((a (make-array (length lst) :initial-contents lst))
         (index (position 'foo a)))
    (cond ((null index) a)
          (t (cons (loop for i from 0 to (1- index)
                      collect (aref a i))
               (list (loop for i from (1+ index) to (1- (length a))
                        collect (aref a i))))))))
  • 从列表中创建一个数组,以便更容易访问这些元素
  • 检查foo是否存在,是否标记了索引
  • 使用loop创建两个列表,一个是foo之前的元素,另一个是foo之后的元素,并将它们合并在一起
    • 在这里,我也尝试过!:)

      但是有一件事你需要澄清:在一些极端情况下,比如:
      foo
      是列表的第一个元素,你应该返回两个列表还是只返回第二个?如果
      foo
      是列表中的最后一个元素,您应该返回list和
      nil
      还是只返回第一个列表?如果
      foo
      不在列表中,您应该只返回列表,还是只返回列表和
      nil
      /
      nil
      和列表

      (defun split (list &key (on-symbol 'foo))
        (let (result result-head)
          (mapl
           #'(lambda (a)
               (if (eql (car a) on-symbol)
                   (return-from split
                     (if result
                         (values result (copy-list (cdr a)))
                         (copy-list (cdr a))))
                   (if result
                       (setf (cdr result-head) (list (car a))
                             result-head (cdr result-head))
                       (setf result (list (car a))
                             result-head result)))) list) result))
      
      (split '(1 2 3 4 5 foo a b c))
      (split '(foo 1 2 3 4 5 foo a b c))
      (split '(1 2 3 4 5 a b c))
      

      以下是我对它的看法,只使用列表:

      (defun split (lst)
        (labels ((split-rec (lst a)
          (cond
            ((or (null lst)
                 (eq (car lst) 'foo))
                   (values (reverse a) (cdr lst)))
            (t (split-rec (cdr lst) (cons (car lst) a))))))
      
          (split-rec lst ())))
      
      split
      将大部分工作卸载到
      split rec
      (在
      labels
      调用中定义),它递归地使用令牌列表,直到到达列表的末尾,或看到“foo”。此时,它会立即获取列表的其余部分,并将其视为第二个列表。因为第一个列表(a)是递归建立的,所以splitrec必须在返回它之前将其反转

      以下是REPL中的两条运行路径:

      > (split '(with great power foo comes great responsibility))
      (WITH GREAT POWER) ;
      (COMES GREAT RESPONSIBILITY)
      
      > (split '(with great power comes great responsibility))
      (WITH GREAT POWER COMES GREAT RESPONSIBILITY) ;
      NIL
      
      > (split nil)
      NIL ;
      NIL
      
      > (split '(with great power foo comes great foo responsibility) :on 'foo)
      (COMES GREAT) ;
      (WITH GREAT POWER RESPONSIBILITY)
      
      > (split '(foo with great power comes great responsibility) :on 'foo)
      NIL ;
      (WITH GREAT POWER COMES GREAT RESPONSIBILITY)
      
      我能想到的大多数边缘案例都得到了处理,并且总是返回两个列表。呼叫者可以使用
      多值绑定
      获取两个列表,即:

      (multiple-value-bind (a b)
        (split '(with great power foo comes great responsibility))
          ; do something useful with a and b
          )
      

      这是家庭作业吗?如果是这样,请将其标记为这样。请正确缩进代码。只需谷歌“lisp缩进”或“lisp风格指南”来了解它是如何完成的。你的代码对于那些你需要帮助的人(以及你自己,在你习惯了它之后)会更容易阅读。像Emacs这样的编辑器会自动执行此操作,并显示匹配的参数。Lisp代码不是通过对齐排列来读取的,而是通过缩进来读取的。对不起,我正在学习。我已经习惯了C评论。我确实遇到过描述如何评论的内容。让我想起了装配评论。没问题。像这样的事情应该让你开始。(如果您使用的是Emacs,只需忽略此处给出的安装提示,然后使用)。