【问题标题】:Does Common Lisp have a something like java's Set Interface/implementing classes?Common Lisp 是否有类似 java 的 Set Interface/implementing classes 的东西?
【发布时间】:2010-09-15 00:17:00
【问题描述】:

我需要像this 这样的东西,它是一个元素集合,其中不包含任何元素的重复项。 Common Lisp,特别是 SBCL,有这样的东西吗?

【问题讨论】:

    标签: java lisp set sbcl


    【解决方案1】:

    我不知道,但你可以使用hash tables 来做类似的事情。

    【讨论】:

      【解决方案2】:

      Lisp 哈希表是基于 CLOS 的。规格here.

      【讨论】:

      • 嗯... CL 的标准哈希表 CLOS 类HASH-TABLE 的实例,您可以毫无顾虑地为其定义方法。此外,您链接到的文章讨论了一种根本不像 CLOS 的对象系统。在 CLOS 中,方法属于泛型函数,而不是对象。
      • Common Lisp 哈希表包装在 CLOS 中。我不知道它们在什么意义上是“基于 CLOS”的。例如,CLTL1 有哈希表但没有 CLOS。
      【解决方案3】:

      如前所述,只需使用哈希表即可快速解决。

      但是,如果您更喜欢更有原则的方法,您可以查看FSet,它是“功能集合论集合库”。其中,它包含集合和包的类和操作。

      (编辑:)最简洁的方法可能是将面向集合的操作定义为通用函数。毕竟,一组泛型函数基本上相当于一个 Java 接口。您可以简单地在标准 HASH-TABLE 类上实现方法作为第一个原型,并允许其他实现。

      【讨论】:

        【解决方案4】:

        您可以使用列表,但事实证明它们在表示大型集合时效率低下。这是使用 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 操作对应的是:

        【讨论】:

        • 这是一种推荐的方法,先开始,然后在分析表明需要它时进行优化。
        【解决方案5】:

        看看cl-containers。有一个集合容器类。

        【讨论】:

          【解决方案6】:

          是的,它有套装。请参阅 Practical Common Lisp 中的 this section on "Sets"

          基本上,您可以使用pushnewadjoin 创建一个集合,使用membermember-ifmember-if-not 对其进行查询,并将其与其他具有intersectionunion 等函数的集合结合使用、set-differenceset-exclusive-orsubsetp

          【讨论】:

            【解决方案7】:

            使用哈希表很容易解决。

            (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
            

            【讨论】:

              【解决方案8】:

              就我个人而言,我只会实现一个函数,它接受一个列表并返回一个唯一的集合。我一起起草了一些对我有用的东西:

              (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 HyperSpecCommon Lisp the Language (2nd Edition) 的组合。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2016-08-02
                • 1970-01-01
                • 2011-04-20
                • 2011-01-07
                • 1970-01-01
                • 2011-06-12
                • 1970-01-01
                相关资源
                最近更新 更多