Skip to content

Instantly share code, notes, and snippets.

@vapniks
Created June 20, 2013 14:50
Show Gist options
  • Save vapniks/5823399 to your computer and use it in GitHub Desktop.
Save vapniks/5823399 to your computer and use it in GitHub Desktop.
A mapping function that allows splicing lists into the result. If fun returns a list whose first element is @ the following elements will be spliced into the list.
(defun mapcar@ (fun seq)
(let (result)
(loop for elem in (reverse seq)
for newelem = (funcall fun elem)
if (and (listp newelem)
(eq (car newelem) '@))
do (loop for newelem2 in (cdr newelem)
do (setq result (cons newelem2 result)))
else do (setq result (cons newelem result)))
result))
@magnars
Copy link

magnars commented Jun 20, 2013

Huh, that is a cool idea. This is a case I often encounter, so I definitely see the need. But I am not sure the code looks any better than with mapcat? Here's my example, maybe you have a better one. :)

(-mapcat (lambda (b)
           (case b
             (t   (list (function-that-returns-a-value)))
             (nil (function-that-returns-a-list))))
         seq)

(mapcar@ (lambda (b)
           (case b
             (t   (function-that-returns-a-value))
             (nil (cons '@ (function-that-returns-a-list)))))
         seq)

You have to wrap one of the cases either way. Unless you write functions specifically for use with mapcar@ that always have a '@ in front, but that seems slightly silly to me.

What do you think?

@vapniks
Copy link
Author

vapniks commented Jun 20, 2013

Ah yes, I wasn't aware of -mapcat. Anyway, how about a further generalization for performing the converse of splicing: folding over a subsequence of seq, and emitting the result of the fold (thus returning a single item instead of many). I was thinking of introducing another special symbol, e.g. 'omit, to indicate results to omit. A closure over some accumulator variable could be passed into mapcar@.

@magnars
Copy link

magnars commented Jun 21, 2013

An example usage would be great. :)

@vapniks
Copy link
Author

vapniks commented Jun 21, 2013

For example you could use it for counting subsequences:

(mapcar@ '(closure (acc prev) (b)
                   (cond b
                         ((eq prev nil) (setq prev b acc (cons b acc)))
                         ((eq prev b) (setq acc (cons b acc))
                          'omit)
                         (t (prog1 (length acc)
                              (setq acc nil prev b)))))
         seq)

(a a a b b b c d d d) => (3 2 1 3)

Actually, it would also need something to signal the end of the list, and I guess there is probably another mapping function that could do this, but it just seems the logical next step - ragged mapping.
Btw.. how do you get coloured syntax highlighting in your gist?

@vapniks
Copy link
Author

vapniks commented Jun 22, 2013

Actually forget it. Just realized it's probably easier to do it directly with a loop.

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