Common Lisp是否具有类似于Java的Set Interface / implementing类的东西?
我需要这样的东西,一个元素集合,其中不包含任何元素的重复项。 Common Lisp,特别是SBCL,有这样的东西吗?
解决方案
并不是我所知道的,但是我们可以将散列表用于类似的东西。
Lisp哈希表基于CLOS。规格在这里。
对于快速解决方案,只需使用哈希表(如前所述)即可。
但是,如果我们喜欢更原则的方法,则可以看一下FSet,它是一个功能强大的集合理论集合库。除其他外,它包含套件和袋子的类和操作。
(编辑:)最干净的方法可能是将面向集合的操作定义为泛型函数。毕竟,一组通用函数基本上等效于Java接口。我们可以简单地在标准HASH-TABLE类上实现方法作为第一个原型,并允许其他实现。
我们可以使用列表,尽管它们可能无法有效地表示大型集合。使用ADJOIN或者PUSHNEW将新元素添加到列表中,然后使用DELETE或者REMOVE进行相反操作即可完成此操作。
(let ((set (list))) (pushnew 11 set) (pushnew 42 set) (pushnew 11 set) (print set) ; set={42,11} (setq set (delete 42 set)) (print set)) ; set={11}
需要注意的一件事是,这些运算符默认情况下全部使用EQL来测试集合中可能存在的重复项(就像Java使用equals方法一样)。这对于包含数字或者字符的集合是可以的,但是对于其他对象的集合,应将诸如" EQUAL"之类的"更深"相等性测试指定为:TEST关键字参数,例如对于一组字符串:-
(let ((set (list))) (pushnew "foo" set :test #'equal) (pushnew "bar" set :test #'equal) (pushnew "foo" set :test #'equal) ; EQUAL decides that "foo"="foo" (print set)) ; set={"bar","foo"}
Lisp与Java的Set操作相对应的是:
- addAll-> UNION或者NUNION
- containsAll-> SUBSETP
- removeAll-> SET-DIFFERENCE或者NSET-DIFFERENCE
- keepAll->相交或者不相交
看cl容器。有一个集合容器类。
是的,它有套。请参阅《实践通用Lisp》中有关"集合"的这一部分。
基本上,我们可以创建带有pushnew
和adjoin
的集合,并使用member
,member-if
和member-if-not
进行查询,并将其与具有诸如intersection之类的其他集合的组合,
union,
set-difference,
set-exclusive-or和
subsetp`。
使用哈希表可轻松解决。
(let ((h (make-hash-table :test 'equalp))) ; if you're storing symbols (loop for i from 0 upto 20 do (setf (gethash i h) (format nil "Value ~A" i))) (loop for i from 10 upto 30 do (setf (gethash i h) (format nil "~A eulaV" i))) (loop for k being the hash-keys of h using (hash-value v) do (format t "~A => ~A~%" k v)))
输出
0 => Value 0 1 => Value 1 ... 9 => Value 9 10 => 10 eulaV 11 => 11 eulaV ... 29 => 29 eulaV 30 => 30 eulaV
就个人而言,我只是实现一个接受列表并返回唯一集的函数。我一起草拟了一些对我有用的东西:
(defun make-set (list-in &optional (list-out '())) (if (endp list-in) (nreverse list-out) (make-set (cdr list-in) (adjoin (car list-in) list-out :test 'equal))))
基本上,"添加"功能在且仅当列表中不存在该项目时才将其无损地添加到列表中,并接受可选的测试功能(Common Lisp"等于"功能之一)。我们也可以使用pushnew
进行破坏性的操作,但是我发现尾递归实现要优雅得多。因此,Lisp确实导出了一些基本功能,使我们可以将列表作为一组使用。不需要内置的数据类型,因为我们可以使用不同的函数将内容放在列表的前面。
我所有这些数据的来源(不是函数,而是信息)都是Common Lisp HyperSpec和Common Lisp the Language(第二版)的组合。