2

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、文字列では戻ります。

どうやら私が理解していない文字列と数値の間にはコアの違いがあります。memberやなどのリスト処理関数はset-difference、なぜそれらに対して異なる働きをするのでしょうか?

4

3 に答える 3

1

リストと文字列の両方がシーケンスであるため、文字列は数値よりもリストに関連しています。

"Hello"プリミティブ文字値#\Hで始まり、 で終わるシーケンス (複合データ型)#\oです。

'(1 2 3)プリミティブ数値 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。それらをシーケンスで正しく動作させるには、#'equalas として指定する必要があります:test

于 2013-08-13T22:10:15.500 に答える
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
于 2013-08-13T09:52:41.760 に答える