【翻译】Lisp.集合

7 views
Skip to first unread message

panfei

unread,
Oct 15, 2012, 3:19:09 AM10/15/12
to lisp-...@googlegroups.com

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

[plain] view plaincopy
  1. [1]> (member 'b '(a b c))  
  2. (B C)  


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


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


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

[plain] view plaincopy
  1. [3]> (member '(a) '((a) (z)))  
  2. NIL  
  3. [4]> (member '(a) '((a) (z)) :test #'equal)  
  4. ((A) (Z))  
  5. [5]>   

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


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

[plain] view plaincopy
  1. [8]> (member 2 '((1) (2)))  
  2. NIL  
  3. [9]> (member 2 '((1) (2)) :key #'car)  
  4. ((2))  
  5. [10]> (member 2 '((1) (2)) :key #'car :test #'equal)  
  6. ((2))  
  7. [11]> (member 2 '((1) (2)) :test #'equal :key #'car)  
  8. ((2))  

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


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

[plain] view plaincopy
  1. [12]> (member-if #'oddp '(2 3 4 5))  
  2. (3 4 5)  

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

[plain] view plaincopy
  1. (defun our-member-if (fn lst)  
  2.   (and (consp lst)  
  3.     (if (funcall fn (car lst))  
  4.       lst  
  5.     (our-member-if fn (cdr lst)))))  

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

[plain] view plaincopy
  1. [15]> (adjoin 'b '(a b c))  
  2. (A B C)  
  3. [16]> (adjoin 'z '(a b c))  
  4. (Z A B C)  

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


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

[plain] view plaincopy
  1. [17]> (union '(a b c) '( c b s))  
  2. (A C B S)  
  3. [18]> (intersection '(a b c) '(b b c))  
  4. (B C)  
  5. [19]> (set-difference '(a b c d e) '(b e))  
  6. (A C D)  

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

--
不学习,不知道

Reply all
Reply to author
Forward
0 new messages