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