;;;; Written & tested in SBCL, will need adjustment for Elisp and (require 'cl); ;;;; Elisp's FORMAT has different meaning and format strings! ;;;; My previous version of breadth-first traversal made new cons cells around ;;;; the original tree's cells, without their cdrs, when adding them to the queue. ;;;; E.g., for (1 2 3 4), only (2), (3), and (4) went into the queue, instead ;;;; of the original tree's cons cell pointers (2 3 4), (3 4), (4). ;;;; This made handling the queue easier, but lost me the ability to operate ;;;; on the tree---I could only collect its leaves. ;;;; Running the queue of only the actual tree's cons cells requires not adding ;;;; any siblings twice when processing a node if they have been added before. ;;;; Hence the hash table for keeping track. ;;; This function does a BFS traversal of the tree, changing every node's atom ;;; into the result of calling func on it. (defun bfs-modify (l func) (let ((q (list l)) (visited-nodes (make-hash-table))) (labels ( ;; add nodes from lst to queue, destructively ;; only add nodes not in the visited nodes table (add-to-queue (queue lst) (let ((siblings (mapcon #'list lst))) ; same as (make-lst-of-cdrs lst))) ;; add siblings not yet in the queue to the queue (let ((upd-queue (nconc queue (remove-if (lambda (s) (gethash s visited-nodes)) siblings)))) ;; now add these to the hash (mapcar (lambda (s) (setf (gethash s visited-nodes) t)) siblings) upd-queue))) ; must return the updated value, not result of mapcar ;; actual traversal (bfs-aux (q) ; queue processor, instead of the loop (format t "q: ~S l: ~S ~%" q l) (if (not (null q)) ; if no more nodes on the queue, return the result list (let ((n (car q)) ; get next node off the queue (q (cdr q))) ; ..and adjust the queue (if (atom (car n)) ; if the node is a leaf (progn (format t " branch1 cdr_n: ~S~%" (cdr n)) (rplaca n (funcall func (car n))) ; ...apply func to it and (bfs-aux (add-to-queue q (cdr n)))) ; ...add its siblings to queue ;; otherwise it's not a leaf, add it to queue _after_ siblings (bfs-aux (add-to-queue (add-to-queue q (cdr n)) ; add siblings (car n)))))))) ; add the non-leaf to the back (bfs-aux q) l))) ;; testing (bfs-modify '(1) (lambda (x) 'z)) (bfs-modify nil (lambda (x) 'z)) ; fix this! (bfs-modify '(1 (2) 3) (lambda (x) 'z)) (bfs-modify '(((((1)) 2)) 4 (((2) (3 5)))) (lambda (x) 'z)) ;;;;---------------------------------------------------------------------------------------------- ;;; for a list, make a list of cdrs of the original list ;;; This is actually just (MAPCON #'LIST l) , but I only found out after I wrote this one. (defun make-lst-of-cdrs (l) (labels ((walk-cdrs (x acc) (if (null (cdr x)) acc (walk-cdrs (cdr x) (cons (cdr x) acc))))) (if (null l) nil ; we might get an empty list passed in, so hand it right back (nreverse (walk-cdrs l (list l)))))) ; make-lst-of-cdrs (setq ll '(1 2 3 4)) ;(1 2 3 4) (make-lst-of-cdrs ll) ;((2 3 4) (3 4) (4)) (make-lst-of-cdrs nil)