vendor/quickutils.lisp @ 829e38d1f825
Fuck a `LOOP`
| author | Steve Losh <steve@stevelosh.com> |
|---|---|
| date | Fri, 10 Feb 2017 21:11:38 +0000 |
| parents | a66997c0fad3 |
| children | 55e8aef75bee |
;;;; This file was automatically generated by Quickutil. ;;;; See http://quickutil.org for details. ;;;; To regenerate: ;;;; (qtlc:save-utils-as "quickutils.lisp" :utilities '(:DEFINE-CONSTANT :ENSURE-BOOLEAN :N-GRAMS :RANGE :SWITCH :WITH-GENSYMS) :ensure-package T :package "EULER.QUICKUTILS") (eval-when (:compile-toplevel :load-toplevel :execute) (unless (find-package "EULER.QUICKUTILS") (defpackage "EULER.QUICKUTILS" (:documentation "Package that contains Quickutil utility functions.") (:use #:cl)))) (in-package "EULER.QUICKUTILS") (when (boundp '*utilities*) (setf *utilities* (union *utilities* '(:DEFINE-CONSTANT :ENSURE-BOOLEAN :TAKE :N-GRAMS :RANGE :STRING-DESIGNATOR :WITH-GENSYMS :EXTRACT-FUNCTION-NAME :SWITCH)))) (defun %reevaluate-constant (name value test) (if (not (boundp name)) value (let ((old (symbol-value name)) (new value)) (if (not (constantp name)) (prog1 new (cerror "Try to redefine the variable as a constant." "~@<~S is an already bound non-constant variable ~ whose value is ~S.~:@>" name old)) (if (funcall test old new) old (restart-case (error "~@<~S is an already defined constant whose value ~ ~S is not equal to the provided initial value ~S ~ under ~S.~:@>" name old new test) (ignore () :report "Retain the current value." old) (continue () :report "Try to redefine the constant." new))))))) (defmacro define-constant (name initial-value &key (test ''eql) documentation) "Ensures that the global variable named by `name` is a constant with a value that is equal under `test` to the result of evaluating `initial-value`. `test` is a function designator that defaults to `eql`. If `documentation` is given, it becomes the documentation string of the constant. Signals an error if `name` is already a bound non-constant variable. Signals an error if `name` is already a constant variable whose value is not equal under `test` to result of evaluating `initial-value`." `(defconstant ,name (%reevaluate-constant ',name ,initial-value ,test) ,@(when documentation `(,documentation)))) (defun ensure-boolean (x) "Convert `x` into a Boolean value." (and x t)) (defun take (n sequence) "Take the first `n` elements from `sequence`." (subseq sequence 0 n)) (defun n-grams (n sequence) "Find all `n`-grams of the sequence `sequence`." (assert (and (plusp n) (<= n (length sequence)))) (etypecase sequence ;; Lists (list (loop :repeat (1+ (- (length sequence) n)) :for seq :on sequence :collect (take n seq))) ;; General sequences (sequence (loop :for i :to (- (length sequence) n) :collect (subseq sequence i (+ i n)))))) (defun range (start end &key (step 1) (key 'identity)) "Return the list of numbers `n` such that `start <= n < end` and `n = start + k*step` for suitable integers `k`. If a function `key` is provided, then apply it to each number." (assert (<= start end)) (loop :for i :from start :below end :by step :collecting (funcall key i))) (deftype string-designator () "A string designator type. A string designator is either a string, a symbol, or a character." `(or symbol string character)) (eval-when (:compile-toplevel :load-toplevel :execute) (defmacro with-gensyms (names &body forms) "Binds each variable named by a symbol in `names` to a unique symbol around `forms`. Each of `names` must either be either a symbol, or of the form: (symbol string-designator) Bare symbols appearing in `names` are equivalent to: (symbol symbol) The string-designator is used as the argument to `gensym` when constructing the unique symbol the named variable will be bound to." `(let ,(mapcar (lambda (name) (multiple-value-bind (symbol string) (etypecase name (symbol (values name (symbol-name name))) ((cons symbol (cons string-designator null)) (values (first name) (string (second name))))) `(,symbol (gensym ,string)))) names) ,@forms)) (defmacro with-unique-names (names &body forms) "Binds each variable named by a symbol in `names` to a unique symbol around `forms`. Each of `names` must either be either a symbol, or of the form: (symbol string-designator) Bare symbols appearing in `names` are equivalent to: (symbol symbol) The string-designator is used as the argument to `gensym` when constructing the unique symbol the named variable will be bound to." `(with-gensyms ,names ,@forms)) ) ; eval-when (eval-when (:compile-toplevel :load-toplevel :execute) (defun extract-function-name (spec) "Useful for macros that want to mimic the functional interface for functions like `#'eq` and `'eq`." (if (and (consp spec) (member (first spec) '(quote function))) (second spec) spec)) ) ; eval-when (eval-when (:compile-toplevel :load-toplevel :execute) (defun generate-switch-body (whole object clauses test key &optional default) (with-gensyms (value) (setf test (extract-function-name test)) (setf key (extract-function-name key)) (when (and (consp default) (member (first default) '(error cerror))) (setf default `(,@default "No keys match in SWITCH. Testing against ~S with ~S." ,value ',test))) `(let ((,value (,key ,object))) (cond ,@(mapcar (lambda (clause) (if (member (first clause) '(t otherwise)) (progn (when default (error "Multiple default clauses or illegal use of a default clause in ~S." whole)) (setf default `(progn ,@(rest clause))) '(())) (destructuring-bind (key-form &body forms) clause `((,test ,value ,key-form) ,@forms)))) clauses) (t ,default)))))) (defmacro switch (&whole whole (object &key (test 'eql) (key 'identity)) &body clauses) "Evaluates first matching clause, returning its values, or evaluates and returns the values of `default` if no keys match." (generate-switch-body whole object clauses test key)) (defmacro eswitch (&whole whole (object &key (test 'eql) (key 'identity)) &body clauses) "Like `switch`, but signals an error if no key matches." (generate-switch-body whole object clauses test key '(error))) (defmacro cswitch (&whole whole (object &key (test 'eql) (key 'identity)) &body clauses) "Like `switch`, but signals a continuable error if no key matches." (generate-switch-body whole object clauses test key '(cerror "Return NIL from CSWITCH."))) (eval-when (:compile-toplevel :load-toplevel :execute) (export '(define-constant ensure-boolean n-grams range switch eswitch cswitch with-gensyms with-unique-names))) ;;;; END OF quickutils.lisp ;;;;