首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从序列中删除所有重复元素

从序列中删除所有重复元素
EN

Stack Overflow用户
提问于 2019-06-09 23:59:55
回答 3查看 255关注 0票数 3

通用Lisp序列函数remove-duplicates留下了每个多重性的一个元素。以下类似函数remove-equals的目标是消除所有的多重性。

但是,我希望使用内置函数remove-if (非迭代)和SBCL的哈希表功能,以便使测试函数的时间复杂度保持在O(n)。当前的问题是SBCL等式测试需要是全局的,但是测试也需要依赖于key参数到remove-equals。它能被写成满足这两种要求吗?

代码语言:javascript
复制
(defun remove-equals (sequence &key (test #'eql) (start 0) end (key #'identity))
  "Removes all repetitive sequence elements based on equality test."
  #.(defun equality-test (x y)
      (funcall test (funcall key x) (funcall key y)))
  #.(sb-ext:define-hash-table-test equality-test sxhash)
  (let ((ht (make-hash-table :test #'equality-test)))
    (iterate (for elt in-sequence (subseq sequence start end))
             (incf (gethash (funcall key elt) ht 0)))
    (remove-if (lambda (elt)
                 (/= 1 (gethash elt ht)))
               sequence :start start :end end :key key)))
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-06-10 11:47:01

define-hash-table-test的第三个参数将测试与哈希函数关联起来。使用sxhash并不能达到这个目的,因为它应该适合test函数。(equal x y)暗示着(= (sxhash x) (sxhash))。因此,第二个参数应该是一个函数test-hash,这样(funcall test x y)就意味着(= (test-hash x) (test-hash y))。仅仅拥有测试函数是不可能做到的。也许最好是通过记录它需要有散列支持来绕过整个事情:

代码语言:javascript
复制
(defun remove-duplicated (sequence &key (test #'eql) (start 0) end (key #'identity))
  "Removes all repetitive sequence elements based on equality test.
   equalily tests other than eq, eql, equal and equalp requires you
   add it to be allowed in a hash table eg. sb-ext:define-hash-table-test in SBCL"

  (let ((ht (make-hash-table :test test)))
    (iterate (for elt in-sequence (subseq sequence start end))
             (incf (gethash (funcall key elt) ht 0)))
    (remove-if (lambda (elt)
                 (/= 1 (gethash elt ht)))
               sequence :start start :end end :key key)))

现在,如果用户需要一个自定义测试,那么他们需要自己对它进行测试:

代码语言:javascript
复制
(defun car-equals (a b)
  (equal (car a) (car b)))

(defun car-equals-hash (p)
  (sxhash (car p)))

(sb-ext:define-hash-table-test car-equals car-equals-hash)

(car-equals '(1 2 3 4) '(1 3 5 7)) ; ==> t
(defparameter *ht* (make-hash-table :test 'car-equals))
(setf (gethash '(1 2 3 4) *ht*) 'found)
(gethash '(1 3 5 7) *ht*) ; ==> found

(remove-duplicated '((5 0 1 2) (5 1 2 3) (5 1 3 2) (5 2 3 4)) 
                   :test #'car-equals 
                   :key #'cdr) 
; ==> ((5 0 1 2) (5 2 3 4))
票数 6
EN

Stack Overflow用户

发布于 2019-06-10 08:28:08

像这样的读取时间计算函数不会像你想的那样做。从代码中简化:

代码语言:javascript
复制
(defun foo (a b test)
  #.(defun equality-test (x y)
      (funcall test x y))
  (funcall #'equality-test a b))

,这是不可能的.

原因1:读取时间创建的函数无法访问来自周围代码的词法变量(这里没有方法引用test,因为在读取过程中不存在带有函数foo的环境)

test变量在equality-test中并不是指词法变量。它是未定义的/未声明的。

原因2:DEFUN计算结果为符号

在读取和评估读取时代码后,代码如下所示:

代码语言:javascript
复制
(defun foo (a b test)
   equality-test
   (funcall #'equality-test a b))

equality-test是一个未绑定变量。这是运行时的一个错误。

原因3:函数equality-test可能不存在

如果我们用文件编译器编译代码,函数equality-test是在读取表单时在编译时环境中创建的,但它不会是编译代码的一部分。

票数 5
EN

Stack Overflow用户

发布于 2019-06-10 06:21:04

免责声明:我发现@Sylwester的答案更清晰、更干净--只是更好(没有宏)。

然而,这只是假设(但不是一个好做法):

代码语言:javascript
复制
(ql:quickload :iterate)    ;; you forgot these - but they are necessary
(use-package :iterate)     ;; for your code to run - at least my definition
(ql:quickload :alexandria) ;; of 'minimal working example' is to include imports.

(defmacro remove-equals (sequence &key (test #'eql) (start 0) end (key #'identity))
  "Remove all repetitive sequence alements based on equality test."
  (alexandria:once-only (sequence test start end key) ; as hygyenic macro
    `(progn
       (defun equality-test (x y)
          (funcall ,test (funcall ,key x) (funcall ,key y)))
       (sb-ext:define-hash-table-test equality-test sxhash)
       (let ((ht (make-hash-table :test #'equality-test)))
          (iterate (for elt in-sequence (subseq ,sequence ,start ,end))
                   (incf (gethash (funcall ,key elt) ht 0)))
          (remove-if (lambda (elt)
                       (/= 1 (gethash (funcall ,key elt) ht)))
                     ,sequence :start ,start :end ,end :key ,key)))))

(remove-equals '(1 2 3 1 4 5 3) :test #'= :end 6)
;; WARNING: redefining COMMON-LISP-USER::EQUALITY-TEST in DEFUN
;; 
;; (2 3 4 5 3)

(describe 'equality-test) ;; shows new definition
;; COMMON-LISP-USER::EQUALITY-TEST
;;   [symbol]
;; 
;; EQUALITY-TEST names a compiled function:
;;   Lambda-list: (X Y)
;;   Derived type: (FUNCTION (T T) (VALUES BOOLEAN &OPTIONAL))
;;   Source form:
;;     (SB-INT:NAMED-LAMBDA EQUALITY-TEST
;;         (X Y)
;;       (BLOCK EQUALITY-TEST
;;         (FUNCALL #'= (FUNCALL #1=#<FUNCTION IDENTITY> X)
;;                  (FUNCALL #1# Y))))

警告总是会发生的--如果您使用的不仅仅是一个哈希表,这肯定会干扰并导致错误。所以我不建议你这么做!

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56519209

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档