Lisp.集合

来源:互联网 发布:java aes加密算法 编辑:程序博客网 时间:2024/06/06 12:40

列表是一种表示小集合的好方式。列表中的每一个元素是它所代表的集合中的一个成员:

[1]> (member 'b '(a b c))(B C)


当member返回真的时候,它不是简单地返回t,而是返回以它找的元素为首的列表的一部分。逻辑上,一个cons可以代表t,并且使用这种方式函数能够返回更多的信息。


默认地,member通过eql来比对对象。你可以通过一种叫做关键字参数(keyword argument)的东西来重载它。很过common lisp的函数都接受一个或更多的关键字参数。他们的不同寻常之处在于他们不是按照位置来匹配参数的,而是通过特殊的标签(tag),这些标签叫做关键字,并且必须把它们置于函数调用参数的前面。一个关键字是有冒号前缀的符号(symbol)。


member接受的关键字参数之一是一个:test参数。如果你在member的调用中,向:test参数传递了某个函数,然后这个函数将被用来代替eql来测试相等。所以如果我们想从列表中找到一个成员,这个成员equal一个给丁的对象,我们可以这样:

[3]> (member '(a) '((a) (z)))NIL[4]> (member '(a) '((a) (z)) :test #'equal)((A) (Z))[5]> 

关键字参数永远都是可选的。如果任何一个包含在调用中,它们必须在最后出现;如果有多个关键字参数,它们的顺序是没关系的。


member接受的另一个关键字参数是:key参数。通过提供这个参数你可以指定一个函数,这个函数可以在比对之前应用到这些元素上:

[8]> (member 2 '((1) (2)))NIL[9]> (member 2 '((1) (2)) :key #'car)((2))[10]> (member 2 '((1) (2)) :key #'car :test #'equal)((2))[11]> (member 2 '((1) (2)) :test #'equal :key #'car)((2))

后两个都是在问:谁的car是和2 euqal的。


如果我们想找到一个元素,这个元素符合任意的预期——像是oddp,它奇数返回真——我们可以使用相关的member-if:

[12]> (member-if #'oddp '(2 3 4 5))(3 4 5)

我们可以实现一个限制版本的member-if:

(defun our-member-if (fn lst)  (and (consp lst)    (if (funcall fn (car lst))      lst    (our-member-if fn (cdr lst)))))

函数adjoin就像是一个有条件版的cons。它接受一个对象和一个列表,仅仅在对象不是这个列表成员的时候才把这个对象conses到这个列表中:

[15]> (adjoin 'b '(a b c))(A B C)[16]> (adjoin 'z '(a b c))(Z A B C)

通常它和member函数接受同样的关键字参数。


集合并,交和补的操作被实现为union, intersection和set-difference。这些函数只接受两个列表(但是也接受和member一样的关键字参数)。

[17]> (union '(a b c) '( c b s))(A C B S)[18]> (intersection '(a b c) '(b b c))(B C)[19]> (set-difference '(a b c d e) '(b e))(A C D)

因为在集合中没有排序的概念,这些函数不需要保存元素在原列表中的顺序。比如,set-difference的调用也可以返回(d c a)。这种行为也是正确的