I've got a nested plist structure, for example:
(:title "A title"
:repeat (:row #(:a :b :c)
:column #(:c :a :b))
:spec (:data my-data
:late t))
and I need to set :data
to a different value. The challange is that this key may appear anywhere in the tree, possibly even deeper in the tree than this example. It will only appear once. I know about the access library, but can't use it. I can find the key easy enough using a recursive search:
(defun find-in-tree (item tree &key (test #'eql))
(labels ((find-in-tree-aux (tree)
(cond ((funcall test item tree)
(return-from find-in-tree tree))
((consp tree)
(find-in-tree-aux (car tree))
(find-in-tree-aux (cdr tree))))))
(find-in-tree-aux tree)))
But I can't quite work out if there's any way to get the place when it's nested in the tree. Ideally something like:
(setf (find-place-in-tree :data tree) 'foo)
is what I'm after.
Any ideas?
I could not work out your recursive searcher so I wrote a simpler one, which also solves the 'item is present but value is nil
' in the usual way:
(defun find-in-tree (item tree &key (test #'eql))
;; really just use iterate here
(labels ((fit-loop (tail)
(cond
((null tail)
;; not there
(return-from find-in-tree (values nil nil)))
((null (rest tail))
;; not a plist
(error "botched plist"))
(t
(destructuring-bind (this val . more) tail
(cond
((funcall test this item)
;; gotit
(return-from find-in-tree (values val t)))
((consp val)
;; Search in the value if it's a list
(fit-loop val)
(fit-loop more))
(t
;; just keep down this list
(fit-loop more))))))))
(fit-loop tree)))
Given that the setf
function is essentially trivial if you don't want it to add entries (which it can not always do anyway):
(defun (setf find-in-tree) (new item tree &key (test #'eql))
;; really just use iterate here
(labels ((fit-loop (tail)
(cond
((null tail)
(error "not in tree"))
((null (rest tail))
(error "botched plist"))
(t
(destructuring-bind (this val . more) tail
(cond
((funcall test this item)
(return-from find-in-tree
(car (setf (cdr tail) (cons new more)))))
((consp val)
(fit-loop val)
(fit-loop more))
(t
(fit-loop more))))))))
(fit-loop tree)))