2009-12-08 28 views
2

给定一个列表,我尝试返回一个新的项目,该项目只有在我作为参数接收的第一个列表中出现多次的项目。作业:在列表中多次出现的Lisp项目

我也做了以下内容:

(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”)?

+2

链式IF形式可以更好地转换为COND。 – Svante 2009-12-08 16:59:18

回答

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)))) 

反向 S中的结果,当递归结束。

现在,在下一步之前如何从列表中删除重复的元素?我们可以写我们的功能是这样的:

(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) 

请注意,我们不再荷兰国际集团的结果,而不是我们只是创建一个新的“cons cell”(cons (first lst) result)consadjoin可以做同样的事情,但只有当它不在列表中时,adjoin才会添加该值。

希望这给你一些玩的东西。

+0

谢谢你的解释 – daniels 2009-12-08 20:47:02

3

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

所以解决这个需要检查A是否已经是结果列表的成员。也就是说,如果第一个列表不是列表的成员,那么将第一个列表追加到列表中。

2

问题似乎是,在追加到元素之前,您没有检查元素是否存在于输出列表中。

+0

是的,这就是我不知道它,如何检查,而不使用变量(即:设置,让等..) – daniels 2009-12-08 15:28:08

+0

如何将A的所有实例从“其余lista”中删除一旦你'已经选择A作为重复? – Gishu 2009-12-08 15:31:25

3

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

注意:这个答案故意有点模糊,因为它是作业和所有。 :)

1

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

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

1

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

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

在这里,我们遍历它吐到当前列表中的项上迭代和其余项目还有待检验。 when子句决定我们收集物品的情况。

相关问题