家庭作业:在列表中出现多次的Lisp项目

时间:2009-12-08 15:15:35

标签: lisp duplicates

给定一个列表,我试图返回一个新的,只有在我作为参数收到的第一个列表中出现多次的项目。

我做了以下事情:

(defun myf (lista)
  (if (endp lista)
      nil
      (if (member (first lista) (rest lista))
          (append (list (first lista)) (myf (rest lista)))
          (myf (rest lista))))) 

如果我运行以下内容:(myf '(A A B A B C)),则返回(A A B)。 我怎样才能让它只返回一次(即没有双“A”)?

6 个答案:

答案 0 :(得分:3)

问题似乎在于您正在检查列表的第一个元素是否在列表尾部并将其附加到结果列表中。当你来到A的第二个实例时会出现问题,当检查时它是肯定的是它在尾部,因为列表中有第三个A实例。如果输入列表有4个A,那么它将返回3个。

因此,要解决此问题,请检查A是否已经是结果列表的成员。也就是说,如果第一个lista不是list的成员,则将第一个lista追加到列表中。

答案 1 :(得分:3)

一旦您在列表中发现一封信不止一次,您就不需要再次检查它,因此您不需要在列表的其余部分中使用它。所以你可以修改剩下的清单......

注意:这个答案故意有些模糊,因为它是为了完成家庭作业而且所有。 :)

答案 2 :(得分:2)

问题似乎是你在追加它之前没有检查输出列表中是否存在该元素。

答案 3 :(得分:2)

Vincent,Gishu和Jerry暗示你需要先检查项目是否已经在结果列表中,然后再附上,而Derek暗示你可以在看到项目重复时修改原始列表。

在此处阅读adjoinremove函数的文档:

http://www.lispworks.com/documentation/HyperSpec/Body/f_adjoin.htm

http://www.lispworks.com/documentation/HyperSpec/Body/f_rm_rm.htm

HyperSpec是一个非常有用的参考,请添加书签。

这两个函数不会修改它们的参数,而是将结果作为新列表返回。还有其他功能可以修改他们的参数,从而可能更有效,但在这一点上,也许你不应该担心它们。

好的,我希望在我写这篇文章的时候你已经弄明白了。如果没有,继续尝试,这是你真正学习的唯一方法。

现在我想和你谈谈另一种方法,那就是通过递归调用来传递结果。

我们的函数repeated除了调用辅助函数repeatedr之外什么都不会做,它将完成实际的工作,并向它传递一个初始的空结果'()

(defun repeated (lst)
  (repeatedr lst '()))

现在让我们定义辅助函数,它接收两个参数:搜索重复项的列表,以及我们将累积重复项的结果列表。

(defun repeatedr (lst result)
  (if (null lst)
    result
    (if (member (first lst) (rest lst))
      (repeatedr (rest lst) (adjoin (first lst) result))
      (repeatedr (rest lst) result))))

当条件(member (first lst) (rest lst))成立时,我们第一项连接到result,并且该邻接的结果将作为第二项传递给递归调用参数;否则我们只是将result原样传递给递归调用。

当列表最终为空时(if (null lst),我们将返回result

> (repeated '(a a b a b c))
(B A)

请注意,结果为(B A),您可能希望它为(A B)。尝试使用笔和纸来跟踪每次调用时递归调用的执行和参数的值,这将是一个很好的练习,你必须使用adjoin来理解它的行为。如果您想要反转结果,可以像这样修改函数:

(defun repeatedr (lst result)
  (if (null lst)
    (reverse result)
    (if (member (first lst) (rest lst))
      (repeatedr (rest lst) (adjoin (first lst) result))
      (repeatedr (rest lst) result))))

这个反转是递归完成后的结果。

现在,在继续之前从列表中删除重复元素的建议怎么样?我们可以这样编写我们的函数:

(defun repeatedr (lst result)
  (if (null lst)
    result
    (if (member (first lst) (rest lst))
      (repeatedr (remove (first lst) lst) (cons (first lst) result))
      (repeatedr (rest lst) result))))

尝试在REPL中使用remove

> (remove 'a '(a b c d a e f b a d))
(B C D E F B D)

请注意,我们不再 adjoin 结果,而只是使用(cons (first lst) result)创建一个新的“cons cell”consadjoin执行相同的操作,但adjoin仅在列表中尚不存在时才会添加值。

希望这能让你有所作为。

答案 4 :(得分:1)

现在,对于列表中的每个元素,如果该元素未包含在列表的其余部分中,则将该元素添加到结果中。

要获得您显然想要的结果,如果结果中尚未包含结果,则将列表中的每个元素添加到结果中。

答案 5 :(得分:1)

如果订单不是问题,那么您也可以这样做:

(defun my-fun-no-order (lst)
   (loop for (item . rest) on lst
    when (= (count item rest) 0)
    collect item))

这里我们迭代一个列表,将它吐出到正在迭代的当前项目中,其余的项目还有待检查。 when子句确定我们收集项目的情况。