# HG changeset patch # User Steve Losh # Date 1471898131 0 # Node ID 2255eeb67076e27b3ed5aa1ae1ff74673788a05a # Parent 5b5e1b9adbbd262b7a69bb891359538557cde17e Clean up `when-found`, add `if-found` and `gathering` diff -r 5b5e1b9adbbd -r 2255eeb67076 losh.lisp --- a/losh.lisp Mon Aug 22 19:54:09 2016 +0000 +++ b/losh.lisp Mon Aug 22 20:35:31 2016 +0000 @@ -249,6 +249,106 @@ ,@body)) (recur ,@(mapcar #'extract-val bindings))))) +(defmacro when-found (var lookup-expr &body body) + "Perform `body` with `var` to the results of `lookup-expr`, when valid. + + `lookup-expr` should be an expression that returns two values, the first being + the result (which will be bound to `var`) and the second indicating whether + the lookup was successful. The standard `gethash` is an example of a function + that behaves like this. + + If the lookup was successful, `body` will be executed and its value returned. + + Example: + + (multiple-value-bind (val found) (gethash :foo hash) + (when found + body)) + + ; becomes + + (when-found val (gethash :foo hash) + body) + + " + (with-gensyms (found) + `(multiple-value-bind (,var ,found) ,lookup-expr + ;; We could preserve and pass along the value of found as a secondary + ;; return value from the form, but that would kill potential last-call + ;; optimization (and the ability to return multiple values from `body`). + (when ,found + ,@body)))) + +(defmacro if-found (var lookup-expr then else) + "Perform `body` or `else` depending on the results of `lookup-expr`. + + `lookup-expr` should be an expression that returns two values, the first being + the result and the second indicating whether the lookup was successful. The + standard `gethash` is an example of a function that behaves like this. + + If the lookup was successful, `then` will be executed with `var` bound to the + result, and its value returned. + + Otherwise `else` will be executed, without any extra bindings. + + Example: + + (multiple-value-bind (val found) (gethash :foo hash) + (if found + 'yes + 'no)) + + ; becomes + + (if-found val (gethash :foo hash) + 'yes + 'no) + + " + (with-gensyms (found result) + `(multiple-value-bind (,result ,found) ,lookup-expr + (if ,found + (let ((,var ,result)) + ,then) + ,else)))) + +(defmacro gathering (&body body) + "Run `body` to gather some things and return them. + + `body` will be executed with the symbol `gather` bound to a function of one + argument. Once `body` has finished, a list of everything `gather` was called + on will be returned. + + It's handy for pulling results out of code that executes procedurally and + doesn't return anything, like `maphash` or Alexandria's `map-permutations`. + + The `gather` function can be passed to other functions, but should not be + retained once the `gathering` form has returned (it would be useless to do so + anyway). + + Examples: + + (gathering + (dotimes (i 5) + (gather i)) + => + (0 1 2 3 4) + + (gathering + (mapc #'gather '(1 2 3)) + (mapc #'gather '(a b))) + => + (1 2 3 a b) + + " + (with-gensyms (result) + `(let ((,result (make-queue))) + (flet ((gather (item) + (enqueue item ,result))) + (declare (dynamic-extent #'gather)) + ,@body) + (queue-contents ,result)))) + ;;;; Mutation (defun build-zap (place expr env) diff -r 5b5e1b9adbbd -r 2255eeb67076 package.lisp --- a/package.lisp Mon Aug 22 19:54:09 2016 +0000 +++ b/package.lisp Mon Aug 22 20:35:31 2016 +0000 @@ -67,7 +67,11 @@ (:documentation "Utilities for managing control flow.") (:export #:recursively - #:recur)) + #:recur + #:when-found + #:if-found + #:gathering + #:gather)) (defsubpackage #:losh.mutation (:documentation "Utilities for mutating places in-place.")