Recently Written · git

common-lisp-utils

My utilities for Common Lisp programming.

git clone https://github.com/equwal/common-lisp-utils

Log | Files | Refs


utils.lisp (21087 bytes)

1 ;;;; My utilities and toys.
2 (defpackage :utils
3   (:use :cl :cl-user :let-over-lambda)
4   (:shadow :mkstr :symb :self :flatten :aif)
5   (:export :once-only
6 	   :mkstr :symb :self :flatten :aif
7 	   :queue
8 	   :collect
9 	   :doarray
10 	   :pushq
11 	   :popq
12 	   :new
13 	   :end
14 	   :group
15 	   :terminatingp
16 	   :with-gensyms
17 	   :y
18 	   :mkstr
19 	   :pop-off
20 	   :dostring
21 	   :make-stack
22 	   :aif
23 	   :use
24 	   :awhen
25 	   :awhile
26 	   :alist
27 	   :aand
28 	   :aor
29 	   :a+
30 	   :asetf
31 	   :f ;; for y-combinator macro
32 	   :_f
33 	   :abbrev
34 	   :abbrevs
35 	   :mapatoms
36 	   :nthcar
37 	   :y-trace
38 	   :memoize
39 	   :empty
40 	   :defmemo
41 	   :clear-memoize
42 	   :basic-profile
43 	   :prognil
44 	   :definline
45 	   :dolines
46 	   :push-on
47 	   :mvbind
48 	   :dbind
49 	   :last1
50 	   :single
51 	   :append1
52 	   :conc1
53 	   :pack
54 	   :longer
55 	   :filter
56 	   :flatten
57 	   :select
58 	   :before
59 	   :after
60 	   :duplicate
61 	   :split-if
62 	   :most
63 	   :best
64 	   :mostn
65 	   :map0-n
66 	   :map1-n
67 	   :mapa-b
68 	   :map->
69 	   :mapflat
70 	   :mappend
71 	   :mapcars
72 	   :rmapcar
73 	   :fand
74 	   :for
75 	   :lrec
76 	   :rfind-if
77 	   :ttrav
78 	   :trec
79 	   :>casex
80 	   :shuffle
81 	   :interpol
82 	   :compose
83 	   :in
84 	   :inq
85 	   :in-if
86 	   :>case
87 	   :while
88 	   :until
89 	   :allf
90 	   :nilf
91 	   :tf
92 	   :it
93 	   :toggle
94 	   :defanaph
95 	   :make-reader))
96 (in-package :utils)
97 (defmacro with-gensyms (symbols &body body)
98   "Create gensyms for those symbols."
99   `(let (,@(mapcar #'(lambda (sym)
100 		       `(,sym ',(gensym))) symbols))
101      ,@body))
102 ;; Compose would work with with a reader macro for point-free notation.
103 ;;; Memoization:
104 ;;; Code from Paradigms of AI Programming 
105 ;;; Copyright (c) 1991 Peter Norvig
106 ;;; modified.
107 
108 ;;; Make CPS functions automagically
109 ;;; Ex: (cps apply& apply (fn list)); => apply&
110 ;;; Some might call this compile-time  intern uncool. Those people are lame.
111 ;; 
112 (defparameter nilq (make-array 1 :fill-pointer 0 :adjustable t))
113 (defmacro collect ((var list) &body body)
114   "Scan a list, taking one element at a time in the var. Collect vars into a list."
115   (with-gensyms (lst)
116     `(let ((,lst ,list))
117        (mapcar #'(lambda (,var)
118 		   ,@body) ,lst))))
119 (declaim (inline queue pushq popq new end))
120 (defun pushq (a q)
121   (declare (vector q) (optimize (speed 3) (safety 0)))
122   (let ((q2 (if (eql nilq q)
123 		(make-array 1 :fill-pointer 0 :adjustable t)
124 		q)))
125     (vector-push-extend a q2)
126     (values q2 a)))
127 (defun end (q)
128   (declare (vector q))
129   (aref q 0))
130 (defun new (a q)
131   (declare (vector q) (optimize (speed 3) (safety 0)))
132   (concatenate 'vector q (make-array 1 :initial-contents `(,a))))
133 (defun popq (q)
134   (declare (vector q))
135   (values (end q) (setf q (subseq q 1))))
136 (defun queue (&rest things)
137   (labels ((inner (things)
138 	     (declare (list things) (optimize (speed 3) (safety 0)))
139 	     (if (null things)
140 		 nilq
141 		 (multiple-value-bind (a b)
142 		     (pushq (car things) (inner (cdr things)))
143 		   (declare (ignore b)) a))))
144     (inner things)))
145 (defmacro make-stack (&rest array-options)
146   "A very common use of arrays. push-on and pop-off"
147   `(make-array 0 :adjustable t :fill-pointer 0 ,@array-options))
148 (defun use (package)
149   "Save a little typing on startup."
150   (asdf:load-system package) (use-package package))
151 ;; From OnLisp
152 (proclaim '(inline last1 single append1 conc1 empty))
153 (proclaim '(optimize speed))
154 (defun empty (array)
155   (declare (type array array))
156   (zerop (length array)))
157 (defun last1 (lst)
158   (car (last lst)))
159 (defun single (lst)
160   (and (consp lst) (not (cdr lst))))
161 (defun append1 (lst obj) 
162   (append lst (list obj)))
163 (defun conc1 (lst obj)   
164   (nconc lst (list obj)))
165 
166 (defun pack (obj)
167   (if (consp obj) obj (list obj)))
168 (defun mkstr (&rest args)
169   (with-output-to-string (s)
170     (dolist (a args) (princ a s))))
171 (defun symb (&rest args)
172   (values (intern (apply #'mkstr args))))
173 
174 (defun longer (x y)
175   "X>Y==>T Y>X==>NIL."
176   (labels ((compare (x y)
177              (and (consp x) 
178                   (or (null y)
179                       (compare (cdr x) (cdr y))))))
180     (if (and (listp x) (listp y))
181         (compare x y)
182         (> (length x) (length y)))))
183 (defun filter (fn lst)
184   "Return a list of true return values for fn over flat lst."
185   (let ((acc nil))
186     (dolist (x lst)
187       (let ((val (funcall fn x)))
188         (if val (push val acc))))
189     (nreverse acc)))
190 (defun flatten (x)
191   (labels ((rec (x acc)
192              (cond ((null x) acc)
193                    ((atom x) (cons x acc))
194                    (t (rec (car x) (rec (cdr x) acc))))))
195     (rec x nil)))
196 (defun select (fn lst)
197   "Return the first element that matches the function."
198   (if (null lst)
199       nil
200       (let ((val (funcall fn (car lst))))
201         (if val
202             (values (car lst) val)
203             (select fn (cdr lst))))))
204 (defun before (x y lst &key (test #'eql))
205   "Return the sublist where x is before y: (x y ...)"
206   (and lst
207        (let ((first (car lst)))
208          (cond ((funcall test y first) nil)
209                ((funcall test x first) lst)
210                (t (before x y (cdr lst) :test test))))))
211 (defun duplicate (obj lst &key (test #'eql))
212   "See if an object is duplicated in a flat list."
213   (member obj (cdr (member obj lst :test test)) 
214           :test test))
215 (defun split-if (fn lst)
216   "Split the list with the first element of the second list being the match."
217   (let ((acc nil))
218     (do ((src lst (cdr src)))
219         ((or (null src) (funcall fn (car src)))
220          (values (nreverse acc) src))
221       (push (car src) acc))))
222 (defun most (fn lst)
223   "Return the original value of the first max (backward high water mark)."
224   (if (null lst)
225       (values nil nil)
226       (let* ((wins (car lst))
227              (max (funcall fn wins)))
228         (dolist (obj (cdr lst))
229           (let ((score (funcall fn obj)))
230             (when (> score max)
231               (setq wins obj
232                     max  score))))
233         (values wins max))))
234 (defun best (fn lst)
235   (if (null lst)
236       nil
237       (let ((wins (car lst)))
238         (dolist (obj (cdr lst))
239           (if (funcall fn obj wins)
240               (setq wins obj)))
241         wins)))
242 (defun mostn (fn lst)
243   "Returns a list of all the original values of the maxes."
244   (if (null lst)
245       (values nil nil)
246       (let ((result (list (car lst)))
247             (max (funcall fn (car lst))))
248         (dolist (obj (cdr lst))
249           (let ((score (funcall fn obj)))
250             (cond ((> score max)
251                    (setq max    score
252                          result (list obj)))
253                   ((= score max)
254                    (push obj result)))))
255         (values (nreverse result) max))))
256 
257 (defun map0-n (fn n)
258   (mapa-b fn 0 n))
259 (defun map1-n (fn n)
260   "Scanning only 1-n."
261   (mapa-b fn 1 n))
262 (defun mapa-b (fn a b &optional (step 1))
263   "Scanning and mapping inclusive."
264   (map-> fn a #'(lambda (i) (> i b)) #'(lambda (i) (+ step i))))
265 (defun map-> (fn start test-fn succ-fn)
266   "General mapping functions."
267   (do ((i start (funcall succ-fn i))
268        (result nil))
269       ((funcall test-fn i) (nreverse result))
270     (push (funcall fn i) result)))
271 
272 (defun mapflat (fn &rest lists)
273   "Consing mapcon. Sublists are appended instead of listed as maplist would do."
274   (apply #'append (apply #'maplist fn lists)))
275 (defun mappend (fn &rest lsts)
276   "Consing mapcan. Results of fn are appended instead of listed as mapcar would do."
277   (apply #'append (apply #'mapcar fn lsts)))
278 
279 (defun mapcars (fn &rest lsts)
280   "Like appending the lsts arguments before mapcar with a uniaritous fn."
281   (let ((result nil))
282     (dolist (lst lsts)
283       (dolist (obj lst)
284         (push (funcall fn obj) result)))
285     (nreverse result)))
286 
287 (defun rmapcar (fn &rest args)
288   "Map the leaves recursively."
289   (if (some #'atom args)
290       (apply fn args)
291       (apply #'mapcar 
292              #'(lambda (&rest args) 
293                  (apply #'rmapcar fn args))
294              args)))
295 (defun fand (fn &rest fns)
296   "Compose functions with and."
297   (if (null fns)
298       fn
299       (let ((chain (apply #'for fns)))
300         #'(lambda (x) 
301             (and (funcall fn x) (funcall chain x))))))
302 
303 (defun for (fn &rest fns)
304   "Compose functions with or."
305   (if (null fns)
306       fn
307       (let ((chain (apply #'for fns)))
308         #'(lambda (x)
309             (or (funcall fn x) (funcall chain x))))))
310 (defun lrec (rec &optional base)
311   (labels ((self (lst)
312              (if (null lst)
313                  (if (functionp base)
314                      (funcall base)
315                      base)
316                  (funcall rec (car lst)
317                               #'(lambda () 
318                                   (self (cdr lst)))))))
319     #'self))
320 (defun rfind-if (fn tree)
321   "Atomic finding."
322   (if (atom tree)
323       (and (funcall fn tree) tree)
324       (or (rfind-if fn (car tree))
325           (if (cdr tree) (rfind-if fn (cdr tree))))))
326 (defun ttrav (rec &optional (base #'identity))
327   "Traverse a whole tree."
328   (labels ((self (tree)
329              (if (atom tree)
330                  (if (functionp base)
331                      (funcall base tree)
332                      base)
333                  (funcall rec (self (car tree))
334                               (if (cdr tree) 
335                                   (self (cdr tree)))))))
336     #'self))
337 (defun trec (rec &optional (base #'identity))
338   "For TTRAV with short circuiting."
339   (labels 
340     ((self (tree)
341        (if (atom tree)
342            (if (functionp base)
343                (funcall base tree)
344                base)
345  (funcall rec tree 
346                         #'(lambda () 
347                             (self (car tree)))
348                         #'(lambda () 
349                             (if (cdr tree)
350                                 (self (cdr tree))))))))
351     #'self))
352 (defmacro in (obj &rest choices)
353   "Macro tool for comparing code symbols."
354   (let ((insym (gensym)))
355     `(let ((,insym ,obj))
356        (or ,@(mapcar #'(lambda (c) `(eql ,insym ,c))
357                      choices)))))
358 
359 (defmacro inq (obj &rest args)
360   "in with automatic quoting the args."
361   `(in ,obj ,@(mapcar #'(lambda (a)
362                           `',a)
363                       args)))
364 
365 (defmacro in-if (fn &rest choices)
366   "Compile time comparisons like SOME. (in-if #'evenp 1 1)==> nil."
367   (let ((fnsym (gensym)))
368     `(let ((,fnsym ,fn))
369        (or ,@(mapcar #'(lambda (c)
370                          `(funcall ,fnsym ,c))
371                      choices)))))
372 
373 (defmacro >case (expr &rest clauses)
374   "Case statement that evaluates the expr (as indicated by the > name)."
375   (let ((g (gensym)))
376     `(let ((,g ,expr))
377        (cond ,@(mapcar #'(lambda (cl) (>casex g cl))
378                        clauses)))))
379 
380 (defun >casex (g cl)
381   (let ((key (car cl)) (rest (cdr cl)))
382     (cond ((consp key) `((in ,g ,@key) ,@rest))
383           ((inq key t otherwise) `(t ,@rest))
384           (t (error "bad >case clause")))))
385 (defmacro while (test &body body)
386   `(do ()
387        ((not ,test))
388      ,@body))
389 
390 (defmacro until (test &body body)
391   `(do ()
392        (,test)
393      ,@body))
394 
395 (defun shuffle (x y)
396   "Interpolate lists x and y with the first item being from x."
397   (cond ((null x) y)
398         ((null y) x)
399         (t (list* (car x) (car y)
400                   (shuffle (cdr x) (cdr y))))))
401 (defun interpol (obj lst)
402   "Intersperse an object in a list."
403   (shuffle lst (loop for #1=#.(gensym) in (cdr lst)
404 		  collect obj)))
405 
406 ;; p. 162
407 (defmacro allf (val &rest args)
408   "Set each arg to the value."
409   (with-gensyms (gval)
410 		`(let ((,gval ,val))
411 		   (setf ,@(mapcan #'(lambda (a) (list a gval))
412 				    args)))))
413 (defmacro nilf (&rest args) "Set everythhing nil."`(allf nil ,@args))
414 (defmacro tf (&rest args) "Set everything true."`(allf t ,@args))
415 (define-modify-macro toggle () not "Flipping vars.")
416 (defmacro defanaphs (&rest pairs)
417   "Generate anaphoric macros. SBCL complains about :all because of an extra IT."
418   ``(,,@(mapcar #'(lambda (p) `(defanaph ,@(if (consp p) p (list p)))) pairs)))
419 (defmacro defanaph (name &optional &key calls (rule :all))
420   (let* ((opname (or calls (intern (subseq (symbol-name name) 1))))
421 	 (body (case rule
422 		 (:all `(anaphex1 args '(,opname)))
423 		 (:first `(anaphex2 ',opname args))
424 		 (:place `(anaphex3 ',opname args)))))
425     `(defmacro ,name (&rest args)
426        ,body)))
427 (defun anaphex1 (args call)
428   (if args
429       (let ((sym (gensym)))
430 	`(let* ((,sym ,(car args))
431 		(it ,sym))
432 	   (declare (ignorable it))
433 	   ,(anaphex1 (cdr args)
434 		      (append call (list sym)))))
435     call))
436 (defun anaphex2 (op args)
437   `(let ((it ,(car args)))
438      (declare (ignorable it))
439      (,op it ,@(cdr args))))
440 (defun anaphex3 (op args)
441   `(_f (lambda (it)
442 	 (declare (ignorable it))
443 	 (,op it ,@(cdr args))) ,(car args)))
444 (defanaphs a+ alist aand aor
445 	   (aif :calls if :rule :first)
446 	   (awhile :rule :first)
447 	   (awhen :rule :first)
448 	   (asetf :rule :place))
449 (defun after (x z lst &key (test #'eql))
450   "Return the sublist where x is after y: (y x...)"
451   (let ((rest (before z x lst :test test)))
452     (aand rest (member x rest :test test) (cons z it))))
453 (defmacro _f (op place &rest args)
454   "For defining setf macros."
455   (multiple-value-bind (vars forms var set access)
456       (get-setf-expansion place)
457     `(let* (,@(mapcar #'list vars forms)
458 	       (,(car var) (,op ,access ,@args)))
459        ,set)))
460 (defmacro pull (obj place &rest args)
461   "Delete obj from a stored list. Args are delete kwargs."
462   (multiple-value-bind (vars forms var set access)
463       (get-setf-expansion place)
464     (let ((g (gensym)))
465       `(let* ((,g ,obj)
466 	      ,@(mapcar #'list vars forms)
467 	      (,(car var) (delete ,g ,access ,@args)))
468 	 ,set))))
469 (defmacro pull-if (test place &rest args)
470   "Pull on a predicate."
471   (multiple-value-bind (vars forms var set access)
472       (get-setf-expansion place)
473     (let ((g (gensym)))
474       `(let* ((,g ,test)
475 	      ,@(mapcar #'list vars forms)
476 	      (,(car var) (delete-if ,g ,access ,@args)))
477 	 ,set))))
478 (defmacro popn (n place)
479   "Repeated popping from a stored list."
480   (multiple-value-bind (vars forms var set access)
481       (get-setf-expansion place)
482     (with-gensyms (gn glst)
483 		  `(let* ((,gn ,n)
484 			  ,@(mapcar #'list vars forms)
485 			  (,glst ,access)
486 			  (,(car var) (nthcdr ,gn ,glst)))
487 		     (prog1 (subseq ,glst 0 ,gn)
488 		       ,set)))))
489 (defmacro make-reader (name (stream-var dispatch-1 dispatch-2) &body body)
490   "Simplify the reader macro creation process in most cases."
491   (with-gensyms (sub-char numarg)
492     `(progn (defun ,name (,stream-var ,sub-char ,numarg)
493 	(declare (ignore ,sub-char ,numarg))
494 	,@body)
495 	    (set-dispatch-macro-character ,dispatch-1 ,dispatch-2 #',name))))
496 (make-reader my-string (stream #\# #\>)
497   "Select a delimiter perl-style. #><TEST< -> 'TEST'"
498   (do* ((prev #1=(read-char stream nil nil) curr)
499 	(first prev)
500 	(curr #1# #1#)
501 	(str (make-stack :element-type 'character)
502 	     (push-on prev str)))
503        ((char= curr first) str)))
504 (defmacro defmemo (fn args &body body)
505   "Define a memoized function."
506   `(memoize (defun ,fn ,args . ,body)))
507 (defun memo (fn &key (key #'first) (test #'eql) name)
508   "Return a memo-function of fn."
509   (let ((table (make-hash-table :test test)))
510     (setf (get name 'memo) table)
511     #'(lambda (&rest args)
512         (let ((k (funcall key args)))
513           (multiple-value-bind (val found-p)
514               (gethash k table)
515             (if found-p val
516                 (setf (gethash k table) (apply fn args))))))))
517 
518 (defun memoize (fn-name &key (key #'first) (test #'eql))
519   "Replace fn-name's global definition with a memoized version."
520   (clear-memoize fn-name)
521   (setf (symbol-function fn-name)
522         (memo (symbol-function fn-name)
523               :name fn-name :key key :test test)))
524 
525 (defun clear-memoize (fn-name)
526   "Clear the hash table from a memo function."
527   (let ((table (get fn-name 'memo)))
528     (when table (clrhash table))))
529 (defun compose (&rest fns)
530   (let ((fns (butlast fns))
531 	(fn1 (car (last fns))))
532     (if fns
533 	(lambda (&rest args)
534 	  (reduce #'funcall fns :from-end t
535 		  :initial-value (apply fn1 args)))
536 	#'identity)))
537 
538 (defmacro y (lambda-list-args specific-args &rest code)
539   "Exactly like Hoyte's nlet. Not tail-recursive unless the compiler does."
540   `(labels ((f ,lambda-list-args
541 	      . ,code))
542      (f ,@specific-args)))
543 #|Essentially:
544 (y (a b) (10 0)
545      (if (= 0 a) b (f (- a 1) (progn (print b) (+ b 1 )))))
546 ;; Named aster the y-combinator, though really it is just a wrapper over labels.
547 |#
548 (defun mapatoms (function tree)
549   "Perform operations on the atoms of a tree."
550   (y (tree) (tree)
551      (cond ((null tree) nil)
552  	   ((integerp tree) (funcall function tree))
553  	   (t (cons (f (car tree))
554  		    (f (cdr tree)))))))
555 (defun terminatingp (char)
556   "Aux function."
557   (and (member char (list #\Newline #\Space #\Tab #\) #\( #\#) :test #'char-equal) char))
558 (defmacro dolines ((var stream) &body body)
559   `(do ((,var #1=(read-line ,stream nil nil) #1#))
560        ((null ,var))
561      ,@body))
562 (defmacro doread((var stream) &body body)
563   "Read character by character with the speed of line by line reading."
564   (with-gensyms (line count len)
565     `(dolines (,line ,stream)
566        (do* ((,line (concatenate 'string ,line (format nil "~%~%")))
567 	     (,len (length ,line))
568 	     (,count 0 (1+ ,count))
569 	     (,var #2=(elt ,line ,count) #2#))
570 	    ((= (1- ,len) ,count))
571 	 ,@body))))
572 (defmacro once-only ((&rest names) &body body)
573   "A macro-writing utility for evaluating code only once."
574   (let ((gensyms (loop for n in names collect (gensym))))
575     `(let (,@(loop for g in gensyms collect `(,g (gensym))))
576        `(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
577           ,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
578              ,@body)))))
579 (defmacro doarray ((var array) &body body)
580   (with-gensyms (count arr)
581     `(block nil
582        (let ((,arr ,array))
583 	 (do* ((,count 0 (1+ ,count))
584 	       (,var (aref ,arr ,count) (aref ,arr ,count)))
585 	      ((= ,count (1- (length ,arr))) (progn ,@body nil))
586 	   (progn ,@body nil))))))
587 (defmacro dostring ((var string &optional result) &body body)
588   (once-only (string)
589     (with-gensyms (count)
590       `(do* ((,count 0 (1+ ,count)))
591 	    ((= ,count (length ,string)) ,result)
592 	 (let ((,var (elt ,string ,count)))
593 	   ,@body)))))
594 (defun nthcar (n list)
595   "Opposite of nthcdr."
596   (if (or (null list) (<= n 0)) nil
597       (cons (car list) (nthcar (1- n) (cdr list)))))
598 (defmacro y-trace (lambda-list-args specific-args &body code)
599   "Short labels with some tracing. Same as y."
600   (with-gensyms (count)
601     `(let ((,count 0))
602        (y ,lambda-list-args ,specific-args
603 	  (format *trace-output*  "~v~(f ~{ ~S~})~%" (incf ,count) (list ,@lambda-list-args))
604 	  (format *trace-output*  "~v~=> ~S~%" ,count  (progn ,@code))))))
605 (defmacro abbrevs (&rest items)
606   `(progn ,@(mapcar #'(lambda (p) (cons 'abbrev p)) (group items 2))))
607 (defmacro abbrev (long short)
608   `(defmacro ,short (&rest args)
609      `(,',long ,@args)))
610 (abbrevs multiple-value-bind mvbind
611 	 destructuring-bind dbind
612 	 vector-pop pop-off
613 	 set-dispatch-macro-character set-dispatch
614 	 get-dispatch-macro-character get-dispatch
615 	 get-macro-character get-char
616 	 set-macro-character set-char
617 	 symbol-macrolet sym-macrolet
618 	 define-setf-expander defexpand)
619 (defun push-on (elt stack)
620   (vector-push-extend elt stack) stack)
621 (defmacro prognil (&rest forms)
622   "Tired for writing (progn terminal-crashing-return-val nil)?"
623   `(progn ,@forms nil))
624 ;;; Reader macro:
625 ;;; Convert something like #Fa.b.c to #'(lambda (x) (a (b (c x))))
626 
627 ;;; For playing with profiling libs. Of paramount importance are:
628 ;;;    (sb-sprof:with-profiling () code)
629 ;;;    (utils:basic-profile () code)
630 ;;; Want to calculate fibbinacci really fast?
631 ;; (defun fib-fast (n) 
632 ;;   (if (<= n 2) 1
633 ;;       (y (m p1 p2) (3 1 1)
634 ;; 	 (if (= n m) (+ p1 p2)
635 ;; 	     (f (1+ m) (+ p1 p2) p1)))))
636 ;;; Want to calculate fibbinacci really slow?
637 ;; (defun fib-slow (n)
638 ;;   (if (<= n 2) 1
639 ;;       (+ (fib-slow (1- n)) (fib-slow (- n 2)))))
640 ;;; Want to calculate fibbinacci at the same speed as the previous one but
641 ;;; memoize each one? Added bonus of being able to write it more idiomatically.
642 ;;; Takes only about twice as long as the other one, which makes sense since
643 ;;; there are now two major operations (addition and hashing) per call.
644 ;; (defmemo fib-save (n) 
645 ;;   (if (>= 0 n) 1
646 ;;       (+ (fib-save (- n 1)) (fib-save (- n 2)))))
647 #|(prognil (time (fib     100000))); => .35 seconds
648 
649 (prognil (time (fibsave 100000))); => .7 seconds
650 (prognil (time (fibsave 100000)))|#; => 0 seconds
651 #+sbcl (require :sb-sprof)
652 #+sbcl (sb-profile:profile fib-save fib-slow fib-fast)
653 ;#+sbcl 
654 #|(defmacro defwith (name bindings before after)
655   `(defmacro ,name (,bindings . (&body body))
656      ,before
657      ,@body
658      ,after))|#
659 #+sbcl (defmacro basic-profile ((&rest functions)
660 				&body body)
661 	 `(progn (sb-profile:reset)
662 		 (sb-profile:unprofile)
663 		 (sb-profile:profile ,@functions)
664 		 ,@body
665 		 (sb-profile:report)
666 		 (sb-profile:unprofile ,@functions)
667 		 (sb-profile:reset)))
668 ;;; A common idiom is (declaim (inline function)) (defun function ...) so we
669 ;;; so we impelement that here.
670 (defmacro definline (name lambda-list &body body)
671   `(declaim (inline ,name))
672   `(defun ,name ,lambda-list
673      ,@body))
674 (defpackage :equwal
675   (:use :cl :cl-user :utils :uiop)
676   (:shadowing-import-from :utils)
677   (:nicknames :eq))