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))