【发布时间】:2010-09-15 00:17:00
【问题描述】:
我需要像this 这样的东西,它是一个元素集合,其中不包含任何元素的重复项。 Common Lisp,特别是 SBCL,有这样的东西吗?
【问题讨论】:
我需要像this 这样的东西,它是一个元素集合,其中不包含任何元素的重复项。 Common Lisp,特别是 SBCL,有这样的东西吗?
【问题讨论】:
我不知道,但你可以使用hash tables 来做类似的事情。
【讨论】:
Lisp 哈希表是基于 CLOS 的。规格here.
【讨论】:
如前所述,只需使用哈希表即可快速解决。
但是,如果您更喜欢更有原则的方法,您可以查看FSet,它是“功能集合论集合库”。其中,它包含集合和包的类和操作。
(编辑:)最简洁的方法可能是将面向集合的操作定义为通用函数。毕竟,一组泛型函数基本上相当于一个 Java 接口。您可以简单地在标准 HASH-TABLE 类上实现方法作为第一个原型,并允许其他实现。
【讨论】:
您可以使用列表,但事实证明它们在表示大型集合时效率低下。这是使用 ADJOIN 或 PUSHNEW 将新元素添加到列表中完成的,而 DELETE or 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 操作对应的是:
【讨论】:
看看cl-containers。有一个集合容器类。
【讨论】:
是的,它有套装。请参阅 Practical Common Lisp 中的 this section on "Sets"。
基本上,您可以使用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))))
基本上,adjoin 函数将一个项目非破坏性地添加到列表中,当且仅当该项目尚不存在于列表中时,接受可选的测试函数(Common Lisp“相等”函数之一)。您也可以使用pushnew 破坏性地这样做,但我发现尾递归实现要优雅得多。因此,Lisp 确实导出了几个基本函数,允许您将列表用作集合;不需要内置数据类型,因为您可以使用不同的函数将内容添加到列表中。
我的所有这些(不是函数,而是信息)的数据源是Common Lisp HyperSpec 和Common Lisp the Language (2nd Edition) 的组合。
【讨论】: