Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save triclops200/43cde6100b5738235e80 to your computer and use it in GitHub Desktop.
Save triclops200/43cde6100b5738235e80 to your computer and use it in GitHub Desktop.
(defun iterator-from-combos (&rest lists)
"Takes a set of lists, and returns an iterator that iterates through every
possible combo of values where each value is from the list passed at its
index."
(if (contains nil lists) ;; If any of the passed lists are nil, no combos can be made.
(lambda () (values nil t)) ;; There are no combos, return an empty iterator.
(labels ;; Otherwise, build the combos iterator.
((shift (lists base) ;; Shifts the set of lists down by one.
;; Returns two values, the new lists, and whether we have hit the end
(if (not lists) ;; If no lists have been passed, we are done shifting.
(values nil t) ;; Return done value.
(let ((first (car lists)) (rest (cdr lists))) ;;Test if we need to ripple shift.
(if (not (cdr first)) ;; Ripple
(multiple-value-bind (result done) (shift (cdr lists) (cdr base))
(values (cons (car base) result) done)) ;; Otherwise, shift one and return.
(values (cons (cdr first) rest) nil)))))) ;; State variables.
(let ((state lists)) ;;The actual iterator.
(lambda () ;;Check if last iteration.
(if (not state) ;;Stop signal
(values nil t) ;; Temporary storage of this result.
(let ((result (mapcar #'car state))) ;; Shift.
(multiple-value-bind (new done) (shift state lists)
;; The shift function says we are done, set up so next iterations is last iteration.
(if done
(setf state nil)
(setf state new))
(values result nil)))))))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment