Skip to content

Instantly share code, notes, and snippets.

@spacebat
Created January 21, 2018 23:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save spacebat/08c763ed725620dcec1f7baa3660e2b3 to your computer and use it in GitHub Desktop.
Save spacebat/08c763ed725620dcec1f7baa3660e2b3 to your computer and use it in GitHub Desktop.
Generic container iteration
(defgeneric call-with-container-map (container func)
(:documentation "Iterate over the contents of CONTAINER, calling
FUNC with the current element of CONTAINER, followed by the index or
key that corresponds to it."))
(defmethod call-with-container-map ((container array) func)
(loop for index from 0
for value across container
do (funcall func value index)))
(defmethod call-with-container-map ((container list) func)
(loop for index from 0
for value in container
do (funcall func value index)))
(defmethod call-with-container-map ((container hash-table) func)
(loop for index being the hash-keys of container
using (hash-value value)
do (funcall func value index)))
(defmacro with-container-map ((container &optional (value-var '~value) (key-var '~key)) &body body)
`(call-with-container-map ,container (lambda (,value-var ,key-var)
(declare (ignorable ,value-var ,key-var))
,@body)))
@jeosol
Copy link

jeosol commented Jan 22, 2018

Hey @spacebat, it's me over from twitter. Nice abstraction and using defmethods specialized around different container type.

@spacebat
Copy link
Author

Thanks. I like the call-with pattern of having a macro do as little as possible beyond wrapping the body in a lambda and calling a higher order function that does the real work. The runtime cost of possibly creating a closure and calling a function gives you better debugging and easier redefinition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment