[Small-cl-src] Re: Small, occasionally handy piece of code

Steven E. Harris seharris at raytheon.com
Fri Jul 9 12:18:39 EDT 2004


Ingvar <ingvar at cathouse.bofh.se> writes:

> Haven't we all felt the need of generating combinations from a
> collection and calling a function for every combination?

I wrote one a while ago that may cons less because it requires use of
vectors to represent the sequences. The interface to
call-with-combinations is similar to yours, with the argument order
mixed up.

There's a lower-level function call-with-n-combo-slots that is useful
when one has both the source and working "target" vectors in hand. The
target vector is used to present the caller-provided function with the
current combination, similar to your "acc" argument.

-------------- next part --------------
;;; Combinations

(defun call-with-last-combo-slots (function source-vector start end
                                            target-vector pos)
  (loop for i from start below end
        do (setf (aref target-vector pos)
                 (aref source-vector i))
        (funcall function target-vector)))


(defun call-with-n-combo-slots (function source-vector src-start src-end
                                         target-vector tgt-start tgt-end)
  (labels ((rec (function source-vector src-start src-end
                          target-vector tgt-start tgt-end)
             (let ((slots-remaining (- tgt-end tgt-start)))
               (if (= 1 slots-remaining)
                   (call-with-last-combo-slots function
                                               source-vector src-start src-end
                                               target-vector tgt-start)
                   (loop 
                       for i-src from src-start to (- src-end slots-remaining)
                       do (setf (aref target-vector tgt-start)
                                (aref source-vector i-src))
                       (rec function
                            source-vector (1+ i-src) src-end
                            target-vector (1+ tgt-start) tgt-end))))))
    ;; TODO: This check is redundant when called from the function
    ;; below, but we want to protect the core loop above from
    ;; unchecked calls. We could just factor it out into a
    ;; non-exported function.
    (when (> tgt-end tgt-start)
      (rec function
           source-vector src-start src-end
           target-vector tgt-start tgt-end))))


(defun call-with-combinations (function k vector
                                        &key (start 0) (end (length vector)))
  (check-type k (integer 0) "a nonnegative integer")
  (when (not (zerop k))
    ;; TODO: Consider validating start and end.
    (when (< (- end start) k)
      (error "Not enough source slots to fill ~A target slots." k))
    (call-with-n-combo-slots function vector start end
                             (make-array k :element-type (array-element-type vector))
                             0 k)))




#+test
(call-with-n-combo-slots #'print (vector 1 2 3 4 5) 0 5
                         (vector 0 0 0) 0 2)

#+test
(call-with-combinations #'print 3 (vector 1 2 3 4 5))
-------------- next part --------------

-- 
Steven E. Harris        :: seharris at raytheon.com
Raytheon                :: http://www.raytheon.com


More information about the small-cl-src mailing list