changelog shortlog graph tags branches changeset files revisions annotate raw help

Mercurial > core / lisp/std/list.lisp

changeset 651: af486e0a40c9
parent: 8d7aa0af2367
author: Richard Westhaver <ellis@rwest.io>
date: Sat, 14 Sep 2024 22:13:06 -0400
permissions: -rw-r--r--
description: multi-binaries, working on removing x.lisp
1 ;;; std/list.lisp --- List utils
2 
3 ;;; Code:
4 (in-package :std/list)
5 
6 ;; (reexport-from :sb-int
7 ;; :include '(:recons :memq :assq :ensure-list :proper-list-of-length-p :proper-list-p
8 ;; :singleton-p))
9 
10 (defun ensure-car (thing)
11  "If THING is a CONS, its CAR is returned. Otherwise THING is returned."
12  (if (consp thing)
13  (car thing)
14  thing))
15 
16 (defun ensure-cons (cons)
17  "If CONS is a cons, it is returned. Otherwise returns a fresh cons with CONS
18  in the car, and NIL in the cdr."
19  (if (consp cons)
20  cons
21  (cons cons nil)))
22 
23 (define-modify-macro appendf (&rest lists) append
24  "Modify-macro for APPEND. Appends LISTS to the place designated by the first
25 argument.")
26 
27 (define-modify-macro nconcf (&rest lists) nconc
28  "Modify-macro for NCONC. Concatenates LISTS to place designated by the first
29 argument.")
30 
31 (define-modify-macro unionf (list &rest args) union
32  "Modify-macro for UNION. Saves the union of LIST and the contents of the
33 place designated by the first argument to the designated place.")
34 
35 (define-modify-macro nunionf (list &rest args) nunion
36  "Modify-macro for NUNION. Saves the union of LIST and the contents of the
37 place designated by the first argument to the designated place. May modify
38 either argument.")
39 
40 (define-modify-macro reversef () reverse
41  "Modify-macro for REVERSE. Copies and reverses the list stored in the given
42 place and saves back the result into the place.")
43 
44 (define-modify-macro nreversef () nreverse
45  "Modify-macro for NREVERSE. Reverses the list stored in the given place by
46 destructively modifying it and saves back the result into the place.")
47 
48 (declaim (inline delete/swapped-arguments))
49 (defun delete/swapped-arguments (sequence item &rest keyword-arguments)
50  (apply #'delete item sequence keyword-arguments))
51 
52 (define-modify-macro deletef (item &rest keyword-arguments)
53  delete/swapped-arguments
54  "Modify-macro for DELETE. Sets place designated by the first argument to
55 the result of calling DELETE with ITEM, place, and the KEYWORD-ARGUMENTS.")
56 
57 (defun let-binding-transform (bs)
58  (if bs
59  (cons
60  (cond ((symbolp (car bs))
61  (list (car bs)))
62  ((consp (car bs))
63  (car bs))
64  (t
65  (error "Bad let bindings")))
66  (let-binding-transform (cdr bs)))))
67 
68 (defun circular-list (&rest elements)
69  "Creates a circular list of ELEMENTS."
70  (let ((cycle (copy-list elements)))
71  (nconc cycle cycle)))
72 
73 (defun circular-list-p (object)
74  "Returns true if OBJECT is a circular list, NIL otherwise."
75  (and (listp object)
76  (do ((fast object (cddr fast))
77  (slow (cons (car object) (cdr object)) (cdr slow)))
78  (nil)
79  (unless (and (consp fast) (listp (cdr fast)))
80  (return nil))
81  (when (eq fast slow)
82  (return t)))))
83 
84 (defun circular-tree-p (object)
85  "Returns true if OBJECT is a circular tree, NIL otherwise."
86  (labels ((circularp (object seen)
87  (and (consp object)
88  (do ((fast (cons (car object) (cdr object)) (cddr fast))
89  (slow object (cdr slow)))
90  (nil)
91  (when (or (eq fast slow) (member slow seen))
92  (return-from circular-tree-p t))
93  (when (or (not (consp fast)) (not (consp (cdr slow))))
94  (return
95  (do ((tail object (cdr tail)))
96  ((not (consp tail))
97  nil)
98  (let ((elt (car tail)))
99  (circularp elt (cons object seen))))))))))
100  (circularp object nil)))
101 
102 ;;; On Lisp
103 (defun group (source n)
104  (declare (fixnum n))
105  (when (zerop n) (error "zero length"))
106  (labels ((rec (source acc)
107  (let ((rest (nthcdr n source)))
108  (if (consp rest)
109  (rec rest (cons
110  (subseq source 0 n)
111  acc))
112  (nreverse
113  (cons source acc))))))
114  (if source (rec source nil) nil)))
115 
116 (eval-when (:compile-toplevel :execute :load-toplevel)
117  (defun flatten (x)
118  (labels ((rec (x acc)
119  (cond ((null x) acc)
120  #+sbcl
121  ((typep x 'sb-impl::comma) (rec (sb-impl::comma-expr x) acc))
122  ((atom x) (cons x acc))
123  (t (rec
124  (car x)
125  (rec (cdr x) acc))))))
126  (rec x nil))))
127 
128 ;;; cl-bench utils
129 ;; Destructive merge of two sorted lists.
130 ;; From Hansen's MS thesis.
131 (defun merge! (a b predicate)
132  (labels ((merge-loop (r a b)
133  (cond ((funcall predicate (car b) (car a))
134  (setf (cdr r) b)
135  (if (null (cdr b))
136  (setf (cdr b) a)
137  (merge-loop b a (cdr b))))
138  (t ; (car a) <= (car b)
139  (setf (cdr r) a)
140  (if (null (cdr a))
141  (setf (cdr a) b)
142  (merge-loop a (cdr a) b))))))
143  (cond ((null a) b)
144  ((null b) a)
145  ((funcall predicate (car b) (car a))
146  (if (null (cdr b))
147  (setf (cdr b) a)
148  (merge-loop b a (cdr b)))
149  b)
150  (t ; (car a) <= (car b)
151  (if (null (cdr a))
152  (setf (cdr a) b)
153  (merge-loop a (cdr a) b))
154  a))))
155 
156 ;; Stable sort procedure which copies the input list and then sorts
157 ;; the new list imperatively. Due to Richard O'Keefe; algorithm
158 ;; attributed to D.H.D. Warren.
159 (defun sort! (seq predicate)
160  (labels ((astep (n)
161  (cond ((> n 2)
162  (let* ((j (truncate n 2))
163  (a (astep j))
164  (k (- n j))
165  (b (astep k)))
166  (merge! a b predicate)))
167  ((= n 2)
168  (let ((x (car seq))
169  (y (cadr seq))
170  (p seq))
171  (setf seq (cddr seq))
172  (when (funcall predicate y x)
173  (setf (car p) y)
174  (setf (cadr p) x))
175  (setf (cddr p) nil)
176  p))
177  ((= n 1)
178  (let ((p seq))
179  (setf seq (cdr seq))
180  (setf (cdr p) nil)
181  p))
182  (t nil))))
183  (astep (length seq))))
184 
185 ;;; EARLY MACROS
186 (defun g!-symbol-p (s)
187  (and (symbolp s)
188  (> (length (symbol-name s)) 2)
189  (string= (symbol-name s)
190  "G!"
191  :start1 0
192  :end1 2)))
193 
194 (defun o!-symbol-p (s)
195  (and (symbolp s)
196  (> (length (symbol-name s)) 2)
197  (string= (symbol-name s)
198  "O!"
199  :start1 0
200  :end1 2)))
201 
202 (defun o!-symbol-to-g!-symbol (s)
203  (symb "G!"
204  (subseq (symbol-name s) 2)))
205 
206 (defmacro defmacro/g! (name args &rest body)
207  (let ((syms (remove-duplicates
208  (remove-if-not #'g!-symbol-p
209  (flatten body)))))
210  (multiple-value-bind (body declarations docstring)
211  (parse-body body :documentation t)
212  `(defmacro ,name ,args
213  ,@(when docstring
214  (list docstring))
215  ,@declarations
216  (let ,(mapcar
217  (lambda (s)
218  `(,s (gensym ,(subseq
219  (symbol-name s)
220  2))))
221  syms)
222  ,@body)))))
223 
224 (defmacro defmacro! (name args &rest body)
225  (let* ((os (remove-if-not #'o!-symbol-p (flatten args)))
226  (gs (mapcar #'o!-symbol-to-g!-symbol os)))
227  (multiple-value-bind (body declarations docstring)
228  (parse-body body :documentation t)
229  `(defmacro/g! ,name ,args
230  ,@(when docstring
231  (list docstring))
232  ,@declarations
233  `(let ,(mapcar #'list (list ,@gs) (list ,@os))
234  ,(progn ,@body))))))
235 
236 (defmacro defun! (name args &body body)
237  (let ((syms (remove-duplicates
238  (remove-if-not #'g!-symbol-p
239  (flatten body)))))
240  (multiple-value-bind (body declarations docstring)
241  (parse-body body :documentation t)
242  `(defun ,name ,args
243  ,@(when docstring
244  (list docstring))
245  ,@declarations
246  (let ,(mapcar (lambda (s)
247  `(,s (gensym ,(subseq (symbol-name s)
248  2))))
249  syms)
250  ,@body)))))
251 
252 ;; from alexandria
253 (defun set-equal (list1 list2 &key (test #'eql) (key nil keyp))
254  "Returns true if every element of LIST1 matches some element of LIST2 and
255 every element of LIST2 matches some element of LIST1. Otherwise returns false."
256  (let ((keylist1 (if keyp (mapcar key list1) list1))
257  (keylist2 (if keyp (mapcar key list2) list2)))
258  (and (dolist (elt keylist1 t)
259  (or (member elt keylist2 :test test)
260  (return nil)))
261  (dolist (elt keylist2 t)
262  (or (member elt keylist1 :test test)
263  (return nil))))))