0

I have a list of plists such as

'((:atom Toddler :visited nil :on-clauses (0 1))
  (:atom Child   :visited nil :on-clauses 1))

how should I change the :onclauses property on a given :atom? I'd like to update this property, e.g. making the second plist (:atom Child :visited nil :on-clauses (1 2)) (only adding new values, never deleting the old ones).

the best I could do was to create a new list from scratch using

(remove-if-not #'(lambda(record)
                   (equal (getf record :atom) atom))
               *ATOMS*)

to get the initial value, updating it, then using its analogue to get a list without this value, and append both together, but this is probably terribly inneficient (I know premature optimatization is bad, but I'm learning LISP and want to know how to do things properly!)

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
bruno cuconato
  • 164
  • 1
  • 9

2 Answers2

2

Use POSITION to find the plist with the specific :atom, and then REMF to remove the property from that plist.

(defun update-on-clauses (atom new-on-clause)
  (let ((pos (position atom *atoms* 
                       :key #'(lambda (record) (getf record :atom)))))
    (when pos
      (setf (getf (nth pos *atoms*) :on-clauses) new-on-clause))))

It might be simpler to make *ATOMS* an alist that maps atoms to property lists.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • hey @Barmar, thanks for the answer! this doesn't seem to be doing exactly what I wanted, I must have written it up badly. I've figured out that the way I was doing it actually was overkill; as plists are destructively altered, just `setf`ing the list I get from what I wrote makes the `*atoms*` list the way I wanted! – bruno cuconato Jun 05 '17 at 21:08
  • I saw `remove-if-not` in your code, so I thought you wanted to remove the property. – Barmar Jun 05 '17 at 21:54
  • I've updated my answer. Your version won't necessarily work, because `setf of getf` can simply assign to the local variable `atomo` without modifying the list in the global variable `*atoms*`. – Barmar Jun 05 '17 at 22:00
1

these three functions seem to do the trick, as plists are destructively modified.

(defun update-atom(atom ix)
  "adds to the atom's on-clauses property another clause where it was found."
   (let ((atomo (find-atom atom)))
    (setf (getf atomo :on-clauses) (cons ix (get-indices atom)))))

(defun find-atom(atom)   "returns the property list of a given atom"   
  (first (remove-if-not #'(lambda(record) (equal (getf record :atom) atom)) *ATOMS*)))

(defun get-indices(atom)
  "gets indices of clauses where atom is found."
  (getf (find-atom atom) :on-clauses))
bruno cuconato
  • 164
  • 1
  • 9
  • 1
    You could use `(find atom *atoms* :key (lambda (record) (getf record :atom)))` instead of your `FIND-ATOM`. Also the `(setf (getf ...) (cons ix ...))` could be simplified to `(push ix (getf ...))`, making the `GET-INDICES` function unnecessary. – jkiiski Jun 06 '17 at 08:25
  • thanks for the tip, @jkiiski! I didn't know I could use find in that way... although I'm not sure I understand how it works. it applies the lambda function using atom to every list in *atoms*, and returns the ones that have non-empty values? as for your second suggestion, I'm not sure it does what I want, I'll try it soon! – bruno cuconato Jun 06 '17 at 14:01
  • The `FIND` calls the key function (which here returns the value of the `:ATOM` property) on each object in `*ATOMS*` and compares the return value to the first argument (`ATOM`). The first object where the key is `EQL` to `ATOM` is returned. – jkiiski Jun 06 '17 at 14:20
  • great, thanks @jkiiski! I got it now. as for the second suggestion, I don't seem to be able to make this work: `(push 1 (getf '(:atom Child :on-clauses (0 2)) :on-clauses))`. is this what you meant? – bruno cuconato Jun 06 '17 at 18:29
  • Try replacing the `(setf ...)` form with `(push ix (getf atomo :on-clauses))`. – jkiiski Jun 06 '17 at 18:37
  • What problem are you having with it? – jkiiski Jun 06 '17 at 18:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/146002/discussion-between-bruno-cuconato-and-jkiiski). – bruno cuconato Jun 06 '17 at 18:56