Lisp.集合

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

[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)。 这种行为也是正确的



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值