newLISP Fan Club

Forum => newLISP in the real world => Topic started by: cormullion on July 08, 2010, 02:37:32 PM

Title: Remove items from list?
Post by: cormullion on July 08, 2010, 02:37:32 PM
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... :(
Title: Re: Remove items from list?
Post by: Lutz on July 08, 2010, 05:46:11 PM
(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)))
Title: Re: Remove items from list?
Post by: cormullion on July 09, 2010, 08:31:19 AM
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!)
Title: Re: Remove items from list?
Post by: Lutz on July 09, 2010, 10:12:47 AM
pop in reverse order:



(dolist (item (reverse (ref-all '(p-name *) planets match)))
   (pop planets item))
Title: Re: Remove items from list?
Post by: Lutz on July 13, 2010, 04:50:37 AM
.. 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'.