161 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
	
		
			6.5 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
| (in-package :alexandria)
 | |
| 
 | |
| ;;; To propagate return type and allow the compiler to eliminate the IF when
 | |
| ;;; it is known if the argument is function or not.
 | |
| (declaim (inline ensure-function))
 | |
| 
 | |
| (declaim (ftype (function (t) (values function &optional))
 | |
|                 ensure-function))
 | |
| (defun ensure-function (function-designator)
 | |
|   "Returns the function designated by FUNCTION-DESIGNATOR:
 | |
| if FUNCTION-DESIGNATOR is a function, it is returned, otherwise
 | |
| it must be a function name and its FDEFINITION is returned."
 | |
|   (if (functionp function-designator)
 | |
|       function-designator
 | |
|       (fdefinition function-designator)))
 | |
| 
 | |
| (define-modify-macro ensure-functionf/1 () ensure-function)
 | |
| 
 | |
| (defmacro ensure-functionf (&rest places)
 | |
|   "Multiple-place modify macro for ENSURE-FUNCTION: ensures that each of
 | |
| PLACES contains a function."
 | |
|   `(progn ,@(mapcar (lambda (x) `(ensure-functionf/1 ,x)) places)))
 | |
| 
 | |
| (defun disjoin (predicate &rest more-predicates)
 | |
|   "Returns a function that applies each of PREDICATE and MORE-PREDICATE
 | |
| functions in turn to its arguments, returning the primary value of the first
 | |
| predicate that returns true, without calling the remaining predicates.
 | |
| If none of the predicates returns true, NIL is returned."
 | |
|   (declare (optimize (speed 3) (safety 1) (debug 1)))
 | |
|   (let ((predicate (ensure-function predicate))
 | |
| 	(more-predicates (mapcar #'ensure-function more-predicates)))
 | |
|     (lambda (&rest arguments)
 | |
|       (or (apply predicate arguments)
 | |
| 	  (some (lambda (p)
 | |
| 		  (declare (type function p))
 | |
| 		  (apply p arguments))
 | |
| 		more-predicates)))))
 | |
| 
 | |
| (defun conjoin (predicate &rest more-predicates)
 | |
|   "Returns a function that applies each of PREDICATE and MORE-PREDICATE
 | |
| functions in turn to its arguments, returning NIL if any of the predicates
 | |
| returns false, without calling the remaining predicates. If none of the
 | |
| predicates returns false, returns the primary value of the last predicate."
 | |
|   (if (null more-predicates)
 | |
|       predicate
 | |
|       (lambda (&rest arguments)
 | |
| 	(and (apply predicate arguments)
 | |
| 	     ;; Cannot simply use CL:EVERY because we want to return the
 | |
| 	     ;; non-NIL value of the last predicate if all succeed.
 | |
| 	     (do ((tail (cdr more-predicates) (cdr tail))
 | |
| 		  (head (car more-predicates) (car tail)))
 | |
| 		 ((not tail)
 | |
| 		  (apply head arguments))
 | |
| 	       (unless (apply head arguments)
 | |
| 		 (return nil)))))))
 | |
| 
 | |
| 
 | |
| (defun compose (function &rest more-functions)
 | |
|   "Returns a function composed of FUNCTION and MORE-FUNCTIONS that applies its
 | |
| arguments to to each in turn, starting from the rightmost of MORE-FUNCTIONS,
 | |
| and then calling the next one with the primary value of the last."
 | |
|   (declare (optimize (speed 3) (safety 1) (debug 1)))
 | |
|   (reduce (lambda (f g)
 | |
| 	    (let ((f (ensure-function f))
 | |
| 		  (g (ensure-function g)))
 | |
| 	      (lambda (&rest arguments)
 | |
| 		(declare (dynamic-extent arguments))
 | |
| 		(funcall f (apply g arguments)))))
 | |
|           more-functions
 | |
|           :initial-value function))
 | |
| 
 | |
| (define-compiler-macro compose (function &rest more-functions)
 | |
|   (labels ((compose-1 (funs)
 | |
|              (if (cdr funs)
 | |
|                  `(funcall ,(car funs) ,(compose-1 (cdr funs)))
 | |
|                  `(apply ,(car funs) arguments))))
 | |
|     (let* ((args (cons function more-functions))
 | |
|            (funs (make-gensym-list (length args) "COMPOSE")))
 | |
|       `(let ,(loop for f in funs for arg in args
 | |
| 		   collect `(,f (ensure-function ,arg)))
 | |
|          (declare (optimize (speed 3) (safety 1) (debug 1)))
 | |
|          (lambda (&rest arguments)
 | |
|            (declare (dynamic-extent arguments))
 | |
|            ,(compose-1 funs))))))
 | |
| 
 | |
| (defun multiple-value-compose (function &rest more-functions)
 | |
|     "Returns a function composed of FUNCTION and MORE-FUNCTIONS that applies
 | |
| its arguments to each in turn, starting from the rightmost of
 | |
| MORE-FUNCTIONS, and then calling the next one with all the return values of
 | |
| the last."
 | |
|   (declare (optimize (speed 3) (safety 1) (debug 1)))
 | |
|   (reduce (lambda (f g)
 | |
| 	    (let ((f (ensure-function f))
 | |
| 		  (g (ensure-function g)))
 | |
| 	      (lambda (&rest arguments)
 | |
| 		(declare (dynamic-extent arguments))
 | |
| 		(multiple-value-call f (apply g arguments)))))
 | |
|           more-functions
 | |
|           :initial-value function))
 | |
| 
 | |
| (define-compiler-macro multiple-value-compose (function &rest more-functions)
 | |
|   (labels ((compose-1 (funs)
 | |
|              (if (cdr funs)
 | |
|                  `(multiple-value-call ,(car funs) ,(compose-1 (cdr funs)))
 | |
|                  `(apply ,(car funs) arguments))))
 | |
|     (let* ((args (cons function more-functions))
 | |
|            (funs (make-gensym-list (length args) "MV-COMPOSE")))
 | |
|       `(let ,(mapcar #'list funs args)
 | |
|          (declare (optimize (speed 3) (safety 1) (debug 1)))
 | |
|          (lambda (&rest arguments)
 | |
|            (declare (dynamic-extent arguments))
 | |
|            ,(compose-1 funs))))))
 | |
| 
 | |
| (declaim (inline curry rcurry))
 | |
| 
 | |
| (defun curry (function &rest arguments)
 | |
|   "Returns a function that applies ARGUMENTS and the arguments
 | |
| it is called with to FUNCTION."
 | |
|   (declare (optimize (speed 3) (safety 1)))
 | |
|   (let ((fn (ensure-function function)))
 | |
|     (lambda (&rest more)
 | |
|       (declare (dynamic-extent more))
 | |
|       ;; Using M-V-C we don't need to append the arguments.
 | |
|       (multiple-value-call fn (values-list arguments) (values-list more)))))
 | |
| 
 | |
| (define-compiler-macro curry (function &rest arguments)
 | |
|   (let ((curries (make-gensym-list (length arguments) "CURRY"))
 | |
|         (fun (gensym "FUN")))
 | |
|     `(let ((,fun (ensure-function ,function))
 | |
|            ,@(mapcar #'list curries arguments))
 | |
|        (declare (optimize (speed 3) (safety 1)))
 | |
|        (lambda (&rest more)
 | |
|          (declare (dynamic-extent more))
 | |
|          (apply ,fun ,@curries more)))))
 | |
| 
 | |
| (defun rcurry (function &rest arguments)
 | |
|   "Returns a function that applies the arguments it is called
 | |
| with and ARGUMENTS to FUNCTION."
 | |
|   (declare (optimize (speed 3) (safety 1)))
 | |
|   (let ((fn (ensure-function function)))
 | |
|     (lambda (&rest more)
 | |
|       (declare (dynamic-extent more))
 | |
|       (multiple-value-call fn (values-list more) (values-list arguments)))))
 | |
| 
 | |
| (define-compiler-macro rcurry (function &rest arguments)
 | |
|   (let ((rcurries (make-gensym-list (length arguments) "RCURRY"))
 | |
|         (fun (gensym "FUN")))
 | |
|     `(let ((,fun (ensure-function ,function))
 | |
|            ,@(mapcar #'list rcurries arguments))
 | |
|        (declare (optimize (speed 3) (safety 1)))
 | |
|        (lambda (&rest more)
 | |
|          (declare (dynamic-extent more))
 | |
|          (multiple-value-call ,fun (values-list more) ,@rcurries)))))
 | |
| 
 | |
| (declaim (notinline curry rcurry))
 | |
| 
 | |
| (defmacro named-lambda (name lambda-list &body body)
 | |
|   "Expands into a lambda-expression within whose BODY NAME denotes the
 | |
| corresponding function."
 | |
|   `(labels ((,name ,lambda-list ,@body))
 | |
|      #',name))
 |