将项目替换为列表中的每个出现

时间:2015-10-09 02:57:49

标签: list recursion replace lisp common-lisp

我刚开始学习Lisp。我的问题是我每次在列表中发生时都试图替换一个项目。我必须使用递归并且没有任何循环来执行此操作。该函数以(subste x y L)开头。

例子如下:

(subste 7 t '(it is 7)) → (IT IS T)
(subste 7 nil '(7 is not to (7))) → (NIL IS not TO (NIL))
(subste 7 nil '(5 is not (22))) → (5 is not (22))

以下是我的目标:

(defun subste (x y L)
  (cond ((null L) nil)
        ((not (= (car L) x))
         subste (x y (cdr L)))
        ((= (car L) x)
         (let L 'y))))

我已多次运行并多次调整它,但考虑到错误消息提供的信息很少而且只是开始学习Lisp这一事实没有运气。感谢。

2 个答案:

答案 0 :(得分:3)

SUBST已经实现了您想要的行为。你似乎只想要旧元素和新元素的顺序不同,所以一个简单的包装器就可以了:

(defun subste (x y l)
  (subst y x l))

如果您想为自己实现它,下面是一个简单的版本。请注意不同的分支:

  • 递归树总是涉及对树的第一个元素进行递归调用,而对树的其余部分进行另一个调用。
  • 在树的末尾返回nil
  • 如果我们发现要替换的元素,我们会更改它。
  • 对于任何其他原子,我们不需要递归,我们只需返回原子。

这里的例子是:

(defun subste (x y l)
  (cond ((null l) nil)
        ((eql l x) y)
        ((atom l) l)
        (T (cons (subste x y (first l))
                 (subste x y (rest l))))))

此版本不完整:您无法替换非eql(如字符串或子列表)的元素。如果需要,可以将测试函数添加为参数,而不是始终使用EQL

答案 1 :(得分:0)

编写 subst 的实现只需要走树,检查树中的节点是否与相同,并用替换它们新即可。在Common Lisp中,像这样的函数通常采用测试函数来确定元素的比较方式(通常是函数,但我在这里省略了)。这导致了这样的事情:

(defun my-subst (old new tree &key (test 'eql))
  "Returns a completely fresh tree like TREE, but with occurrences of
OLD replaced by NEW.  The resulting tree shares no structure with the 
original TREE, except for leaves."
  (cond
    ;; If the current tree is the OLD element,
    ;; return the NEW element instead.
    ((funcall test old tree) new)
    ;; If the current tree is a cons cell,
    ;; call MY-SUBST recursively on both
    ;; the CAR and CDR of the tree, and
    ;; then CONS the results back together.
    ((consp tree) (cons (my-subst old new (car tree) :test test)
                        (my-subst old new (cdr tree) :test test)))
    ;; Otherwise, just return TREE.
    (t tree)))
(my-subst 'x 42 '(1 x 2 (3 x (4 (x) 5))))
;=> (1 42 2 (3 42 (4 (42) 5)))

现在,该实现返回的树总是全新的。也就是说,即使元素永远不会出现在树中,您仍然会得到一棵完全新鲜的树。根据您的使用情况,这可能是一种浪费,记忆。 检查递归调用的结果是否与其输入相同可能是有益的。在这种情况下,您只需返回输入:

(defun my-subst (old new tree &key (test 'eql))
  "Returns a tree like TREE, but with occurrences of OLD
replaced by NEW.  The resulting tree may share structure
with TREE."
  (cond
    ((funcall test old tree) new)
    ((consp tree)
     (let ((new-car (my-subst old new (car tree) :test test))
           (new-cdr (my-subst old new (cdr tree) :test test)))
       (if (and (eq (car tree) new-car)
                (eq (cdr tree) new-cdr))
           tree
           (cons new-car new-cdr))))
    (t tree)))
(let* ((t1 '(1 x 2 (3 x (4 (x) 5))))
       (t2 (my-subst 'x 42 t1))
       (t3 (my-subst 'y 84 t1)))
  (list (eq t1 t2)
        (eq t1 t3)))

;=> (NIL T) ; t1 != t2, but t1 == t3

你想要的将取决于具体情况,但能够做到这两点都很好。