Page 1 of 1

Remove items from list?

Posted: Thu Jul 08, 2010 9:37 pm
by cormullion
Is it possible to remove items from nested lists just using set-ref-all?

Code: Select 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... :(

Re: Remove items from list?

Posted: Fri Jul 09, 2010 12:46 am
by Lutz

Code: Select all

(dolist (item (ref-all '(p-name *) planets match)) 
	(pop planets item))
now planets has all '(p-name *) removed:

Code: Select all

(("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)))

Re: Remove items from list?

Posted: Fri Jul 09, 2010 3:31 pm
by cormullion
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!)

Re: Remove items from list?

Posted: Fri Jul 09, 2010 5:12 pm
by Lutz
pop in reverse order:

Code: Select all

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

Re: Remove items from list?

Posted: Tue Jul 13, 2010 11:50 am
by Lutz
.. 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:

Code: Select all

> (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:

Code: Select all

(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'.