Remove items from list?

Started by cormullion, July 08, 2010, 02:37:32 PM

Previous topic - Next topic

cormullion

Is it possible to remove items from nested lists just using set-ref-all?


(set 'planets '(("Mercury"
      (p-name "Mercury")
      (diameter 0.382)
      (mass 0.06)
      (radius 0.387)
      (period 0.241)
      (incline 7)
      (eccentricity 0.206)
      (rotation 58.6)
      (moons 0))
  ("Venus"
      (p-name "Venus")
      (diameter 0.949)
      (mass 0.82)
      (radius 0.72)
      (period 0.615)
      (incline 3.39)
      (eccentricity 0.0068)
      (rotation -243)
      (moons 0))
      ))
     
(set-ref-all '(p-name *) planets '() match)
;->
  (
    ("Mercury"
      ()
      (diameter 0.382)
      (mass 0.06)
      (radius 0.387)
      (period 0.241)
      (incline  7)  
      (eccentricity 0.206)  
      (rotation 58.6)  
      (moons 0))  
    ("Venus"
      ()
      (diameter 0.949)
      (mass 0.82)
      (radius 0.72)
      (period 0.615)
      (incline 3.39)  
      (eccentricity 0.0068)  
      (rotation -243)  
      (moons 0)))


The empty lists are close but not perfect... :(

Lutz

#1
(dolist (item (ref-all '(p-name *) planets match))
(pop planets item))

now planets has all '(p-name *) removed:

(("Mercury"
  (diameter 0.382)
  (mass 0.06)
  (radius 0.387)
  (period 0.241)
  (incline 7)
  (eccentricity 0.206)
  (rotation 58.6)
  (moons 0))
 ("Venus"
  (diameter 0.949)
  (mass 0.82)
  (radius 0.72)
  (period 0.615)
  (incline 3.39)
  (eccentricity 0.0068)
  (rotation -243)
  (moons 0)))

cormullion

#2
I didn't see an obvious way for set-ref-all to do it, so I'm quite glad I didn't overlook anything obvious...



But I think your suggestion fails on nested lists - because the list is changed by pop, then subsequent changes cause a list out of bounds error? Or am I mistaken? (I'm processing SXML - the example I gave was a bad choice!)

Lutz

#3
pop in reverse order:



(dolist (item (reverse (ref-all '(p-name *) planets match)))
   (pop planets item))

Lutz

#4
.. just wanted to expand on this. The family 'ref' functions always looks for matches going through a list from left to right, depth first. This means, that the removal of any matched expression found only affects indexes of items following, regardless of nesting, even if matches contain matches in a recursive fashion. Consider this:
> (set 'theList '(a (b) (b (b 2) (b (b 3) (b 4)))))
(a (b) (b (b 2) (b (b 3) (b 4))))
> (ref-all '(b *) theList match)
((1) (2) (2 1) (2 2) (2 2 1) (2 2 2))
> (pop theList '(2 2 2))
(b 4)
> (pop theList '(2 2 1))
(b 3)
> (pop theList '(2 2))
(b)
> (pop theList '(2 1))
(b 2)
> (pop theList '(2))
(b)
> (pop theList '(1))
(b)
> theList
(a)
>

vice versa you can construct the original list from all the items popped and the index vector:
(set 'theList '(a (b) (b (b 2) (b (b 3) (b 4)))))

; the list of the index vectors
(set 'indexList (ref-all '(b *) theList match))

; the list of all popped items
(set 'popped (map (curry pop theList) (reverse (copy indexList))))

; after popping out all matches
theList ;=> (a)

; reconstruct the list from popped items starting with residual list
(map (fn (m i) (push m theList i)) (reverse (copy popped)) indexList)

theList ;=> (a (b) (b (b 2) (b (b 3) (b 4))))

Note, that 'reverse' has to be made non-destructive using 'copy'.