git.fiddlerwoaroof.com
transduce.lisp
bd478be5
 (defpackage :fwoar.transduce
   (:use :cl )
   (:shadow :map :filter)
   (:export #:map
            #:filter
            #:take
            #:compress
            #:build-list
            #:build-array
            #:emitting
            #:build
            #:with-transducer-arities))
 (in-package :fwoar.transduce)
 
 (declaim (inline build-list build-array emitting map filter take))
 
 (defmacro with-transducer-arities ((accum it) &body body)
   (alexandria:with-gensyms (a-p i-p)
     `(lambda (rf)
        (declare (type (function (&optional t t) t) rf))
        (lambda (&optional (,accum nil ,a-p) (,it nil ,i-p))
          (declare (optimize (speed 3) (safety 1) (debug 0))
                   (dynamic-extent ,it))
          (cond
            (,i-p ,@body)
            (,a-p (funcall rf ,accum))
            (t (funcall rf)))))))
 
 (defun map (mapping-fun)
   (with-transducer-arities (accum it)
     (let ((mapping-fun (alexandria:ensure-function mapping-fun)))
       (funcall rf
                accum
                (funcall mapping-fun
                         it)))))
 
 (defun filter (filtering-fun)
   (with-transducer-arities (accum it)
     (let ((filtering-fun (alexandria:ensure-function filtering-fun)))
       (if (funcall filtering-fun it)
           (funcall rf accum it)
           accum))))
 
 (defun take (count)
   (let ((idx 0))
     (with-transducer-arities (accum it)
       (prog1 (if (< idx count)
                  (funcall rf accum it)
                  (funcall rf accum))
         (incf idx)))))
 
 (defun compress (&optional (test 'eql))
   (let ((v (gensym)))
     (with-transducer-arities (accum it)
       (prog1 (if (funcall test v it)
                  accum
                  (funcall rf accum it))
         (setf v it)))))
 
 (defstruct %concat-tree
   head
   tail)
 (defun %flatten-concat-tree (tree)
   (if (null (%concat-tree-head tree))
       (%concat-tree-tail tree)
       (let* ((head (%concat-tree-head tree))
              (head-tail (%concat-tree-tail head)))
         (setf (%concat-tree-tail head)
               (if head-tail
                   (cons head-tail (%concat-tree-tail tree))
                   (%concat-tree-tail tree)))
         (%flatten-concat-tree head))))
 
 (defun build-list (rf)
   (declare (type (function (&optional t t) t) rf)
            (dynamic-extent rf))
   (lambda (&optional (a nil a-p) (i nil i-p))
     (declare (optimize (speed 3) (safety 1) (debug 0)))
     (cond
       (i-p (make-%concat-tree :head a :tail i))
       (a-p (funcall rf (%flatten-concat-tree
                         (make-%concat-tree :head a :tail nil))))
       (t (make-%concat-tree :head nil :tail nil)))))
 
 (defun build-list-mutating (rf)
   (declare (type (function (&optional t t) t) rf)
            (dynamic-extent rf))
   (let ((l (list nil nil)))
     (declare (optimize (speed 3) (safety 1) (debug 0)))
     (lambda (&optional (a nil a-p) (i nil i-p))
       (cond
         (i-p (cdr (rplacd a (list i))))
         (a-p (funcall rf (cdr l)))
         (t l)))))
 
 (defun build-array (rf)
   (declare (type (function (&optional t t) t) rf)
            (dynamic-extent rf))
   (let ((l (make-array 1000 :adjustable t :fill-pointer 0)))
     (lambda (&optional (a nil a-p) (i nil i-p))
       (declare (optimize (speed 3) (safety 1) (debug 0)))
       (cond
         (i-p (vector-push-extend i a)
              a)
         (a-p (funcall rf l))
         (t l)))))
 
 (defun emitting (rf)
   (lambda (&optional (a nil a-p) (i nil i-p))
     (cond
       (i-p (format t "~&~s~%" i)
            a)
       (a-p (funcall rf :done))
       (t :going))))
 
 (defun build (builder xf input)
   (locally (declare (optimize (debug 3)))
     (block nil
       (let ((rf (lambda (&optional (a nil a-p) (i nil i-p))
                   (declare (ignore i))
                   (cond
                     (i-p (error "this shouldn't happen"))
                     (a-p (return a))
                     (t (error "this shouldn't happen."))))))
         (etypecase input
           (list (loop with sub-fun = (funcall xf
                                               (funcall builder rf))
                       for next in input
                       for accum = (funcall sub-fun (funcall sub-fun) next)
                         then (funcall sub-fun accum next)
                       finally (funcall sub-fun accum)))
           (vector (loop with sub-fun = (funcall xf
                                                 (funcall builder rf))
                         for next across input
                         for accum = (funcall sub-fun (funcall sub-fun) next)
                           then (funcall sub-fun accum next)
                         finally (funcall sub-fun accum)))
           (sequence
            (loop with sub-fun = (funcall xf
                                          (funcall builder rf))
                  with sequence = input
                  for (iterator limit from-end)
                    = (multiple-value-list (sb-sequence:make-sequence-iterator sequence))
                      then (list (sb-sequence:iterator-step sequence iterator from-end) limit from-end)
                  for accum
                    = (funcall sub-fun)
                      then (funcall sub-fun accum next)
                  until (sb-sequence:iterator-endp sequence iterator limit from-end)
                  for next = (sb-sequence:iterator-element sequence iterator)
                  finally (funcall sub-fun accum))))))))
 
 (defun tmp ()
   (build 'build-array
          (alexandria:compose (map '1+)
                              (map (lambda (v)
                                     (* v 3)))
                              (filter 'oddp))
          #(1 2 3 4)))