SBCL do-symbols (and loop) returns duplicate elements

I found that SBCL 'do-symbols' (and loop) return duplicate elements.

Test conditions: SBCL 1.1.4 x86 on Windows

First, we define some helper functions:

;; compress from Ansi-Common-Lisp (defun compress (x) (labels ((rec (exn) (if (null x) (if (= 1 n) (list e) (list (list en))) (if (eq e (car x)) (rec e (cdr x) (1+ n)) (cons (if (= 1 n) e (list en)) (rec (car x) (cdr x) 1)))))) (rec (car x) (cdr x) 1))) (compress '(aabcddd)) ;;=> ((A 2) BC (D 3)) ;; This one can make the duplicate items visible: (defun duplicates (list) (remove-if-not #'listp (compress (sort list #'string<)))) (duplicates '(aabcddd)) ;;=> ((A 2) (D 3)) ;; This one use 'do-symbols' iterate each symbol in package, and check the ;; result (defun test-pack-do-symbols (package) (let (r) (do-symbols (s package (duplicates r)) (push sr)))) 

When you call "test-pack-do-symbols" on the package: SB-MOP, you can see duplicate items

 (test-pack-do-symbols :sb-mop) ;;=> ((ADD-METHOD 2) (ALLOCATE-INSTANCE 2) (BUILT-IN-CLASS 2) (CLASS 2) ;; (CLASS-NAME 2) (COMPUTE-APPLICABLE-METHODS 2) (ENSURE-GENERIC-FUNCTION 2) #'2 ;; (GENERIC-FUNCTION 2) (MAKE-INSTANCE 2) (METHOD 2) (METHOD-COMBINATION 2) ;; (METHOD-QUALIFIERS 2) (REMOVE-METHOD 2) (STANDARD-CLASS 2) ;; (STANDARD-GENERIC-FUNCTION 2) (STANDARD-METHOD 2) (STANDARD-OBJECT 2) (T 2)) 

There is another way to iterate characters in a package using a powerful "loop".

 ;; Now I define `test-pack-loop' (defun test-pack-loop (package) (duplicates (loop for s being each symbol in package collect s))) 

When you call the "test package-loop", you will not see duplicate elements.

 (test-pack-loop :sb-mop) ;;=> NIL 

But even a loop can return duplicate elements on some packages, you can use the following code to see the difference between "test-pack-do-symbols" and "test-pack-loop"

 (let (r1 r2) (dolist (p (list-all-packages)) (when (test-pack-do-symbols p) (push (package-name p) r1)) (when (test-pack-loop p) (push (package-name p) r2))) (print r1) (print r2) nil) 

So, is this a bug or is it standard?

+6
source share
3 answers

Refer to Common Lisp Hyperspec , which states

do characters are repeated over the characters available in the package. Expressions can be executed more than once for characters inherited from multiple packages.

+11
source

Hans has already written about the DO-SYMBOLS specification.

The obvious solution is to replace PUSH with PUSHNEW .

 (defun test-pack-do-symbols (package) (let (r) (do-symbols (s package (duplicates r)) (pushnew sr)))) 
+6
source

In addition to Rainer's answer, I suggest a do-unique-symbols macro:

 (defmacro do-unique-symbols (var &optional (package '*package*) result-form &body body) "Like common-lisp:do-symbols, but executes only once per unique symbol." (let ((unique-symbols (gensym))) `(let (,unique-symbols) (do-symbols (symbol ,package) (pushnew symbol ,unique-symbols)) (dolist (,var ,unique-symbols ,result-form) ,@body)))) 

(Uninvited, sorry).

0
source

Source: https://habr.com/ru/post/945908/


All Articles