Common Lisp是否具有类似于Java的Set Interface / implementing类的东西?

时间:2020-03-06 15:03:44  来源:igfitidea点击:

我需要这样的东西,一个元素集合,其中不包含任何元素的重复项。 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》中有关"集合"的这一部分。

基本上,我们可以创建带有pushnewadjoin的集合,并使用membermember-ifmember-if-not进行查询,并将其与具有诸如intersection之类的其他集合的组合,unionset-differenceset-exclusive-orsubsetp`。

使用哈希表可轻松解决。

(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(第二版)的组合。