You could call map-into
;-)
(defun map-into-tree (function tree)
(labels
((recurse (tree)
(typecase tree
(sequence (map-into tree #'recurse tree))
(t (funcall function tree)))))
(recurse tree)))
... or equivalently:
(defun map-into-tree (function tree)
(typecase tree
(sequence (map-into tree (lambda (u) (map-into-tree function u)) tree))
(t (funcall function tree))))
Test:
(map-into-tree #'1+ (copy-tree '((9 10) (8 9 10 11 (12 13) () (11) () 13))))
=> ((10 11) (9 10 11 12 (13 14) NIL (12) NIL 14))
I am not sure what should happen with a tree which contains strings: do we really want to iterate over each character? As a matter of fact, this is what is done here above.
I also notice that map-into works with sequences containing cons cells, but the corresponding map-into-tree does not, even though it uses map-into.
(1 (2 . 3))
is a proper list with two elements, namely 1
and (2 . 3)
. Since map-into
does not recurse into elements, all it does is calling the function on both those elements. In your comment, this was print
, which can print improper lists without problems.
The second element is a sequence: when you call map-into-tree
, the function recursively calls map-into
with this sequence, which happens to be an improper list. map-into
expects a proper sequence and thus fails with improper lists.
Please note that in your question, you said:
a function that aims to map into an arbitrary tree of nested proper lists
A tree with improper lists is not a valid input.
Finally, I notice you call destructive functions on literal data, like so:
(map-into #'print '(1 2))
The quoted list is a constant, modifying it at runtime is undefined behavior. That's why I first used copy-tree
in my example.
So would this work to handle all special cases [...]
Since there is already a typecase in place, it is sufficient to handle the special case of the cons
; that works regardless of the type of value held in the cdr
slot:
(defun map-into-tree (function tree)
(labels
((walk (tree)
(typecase tree
(cons
(prog1 tree
(setf (car tree) (walk (car tree)))
(setf (cdr tree) (walk (cdr tree)))))
(sequence (map-into tree #'walk tree))
(t (funcall function tree)))))
(walk tree)))