Common Lisp中字符串和数字之间的核心区别是什么?

时间:2013-08-13 09:14:29

标签: string list numbers common-lisp

作为CL的新手,我用简单的算法玩了很多。例如,我尝试实现一个删除列表中所有唯一元素的函数。

(1 2 2 3 3 4 5 3) -> (2 2 3 3 3)

首次尝试导致此代码:

(defun remove-unique (items)
  (let ((duplicates (set-difference items (remove-duplicates items :test #'equal))))
    (append duplicates (remove-duplicates duplicates :test #'equal))))

这对字符串有效,但对数字总是返回NIL。阅读更多关于set-difference的内容我已经了解到根本不会使用重复的填充列表,它只是在我的情况下以某种方式工作,所以我放弃了这种方法是不可移植的并且继续前进。

另一种尝试是:

(defun remove-unique (items)
  (loop for item in items 
    when (member item (cdr (member item items)))
    collect item))

这适用于数字,但会为字符串返回NIL

显然,我不理解的字符串和数字之间存在核心差异。为什么列表处理函数(如memberset-difference对它们的工作方式不同?

3 个答案:

答案 0 :(得分:2)

数字,字符和字符串的相等比较确实不同。相等,你应该谨慎使用,因为它更昂贵,结构相等(所以它下降到一些对象)。 eq确实是对象平等。对于大多数情况,eql会对象进行对等,除了数字(它们检查类型和值)和字符(它们检查'value')

有关详情,请参阅equaleqleq的超文本条目。

答案 1 :(得分:1)

(defun remove-unique (items &key (test 'eql))
  (loop
     :with table := (make-hash-table :test test)
     :for element :in items :do
     (setf (gethash element table)
           (1+ (gethash element table 0)))
     :finally
     (return
       (loop
          :for k :being :the :hash-keys :of table
          :using (:hash-value v)
          :when (> v 1) :nconc (make-list v :initial-element k)))))

(defun remove-unique (items &key (test 'eql))
  (loop
     :with table := (make-hash-table :test test)
     :for element :in items :do
     (setf (gethash element table)
           (1+ (gethash element table 0)))
     :finally
     (return
       (loop
          :for element :in items
          :unless (= 1 (gethash element table))
          :collect element))))

我可能会使用第一个变体,因为它从哈希表中读取的次数较少,但您需要检查列表中的项目是否稍后不会被修改。

(remove-unique '("1" "2" "2" "3" "3" "4" "5" "3") :test #'equal)

给出:

("2" "2" "3" "3" "3")

但是

(remove-unique '("1" "2" "2" "3" "3" "4" "5" "3"))

给出:

NIL

答案 2 :(得分:1)

字符串与列表更相关,而不是数字,因为列表和字符串都是序列。

"Hello"是一个序列(compund数据类型),以原始字符值#\H开头,以#\o结尾。

'(1 2 3)是一个序列(compond数据类型),以原始数值1开头,以3结尾。

字符与数字类似,因为它们是原始值。可以使用eql比较原始值,而使用equal

可以比较不是同一个对象的序列
(setq list1 (list 1 2 3))
(setq list2 (list 1 2 3))

(eql list1 list2) 
;==> NIL

(equal list1 list2)
;==> T

;; comparing first element of both lists using eql
(eql (car list1) (car list2))
;==> T

(setq string1 "Hello")
(setq string2 "Hello")

(eql string1 string2)
;==> NIL

(equal string1 string2)
;==> T

;; comparing first character of both strings using eql
(eql (elt string1 0) (elt string2 0))
;==> T

Common Lisp中用于比较某些内容的大多数(如果不是全部)函数通常都有一个可选的命名参数:test,您可以在其中提供元素的比较方式。默认值通常为eql。要使它们与序列正确相关,您需要将#'equal作为:test提供。