Remove a sequence of elements from a list

For the Compleat Fan
Locked
cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Remove a sequence of elements from a list

Post by cormullion »

Given a list with some doubled elements:

Code: Select all

(set 's '(1 1 4 2 2 3 3 4 4 5 5 4 4 3 3 4 2 2 1 1))
What's the best way to remove - for example - the two pairs of 4 4? I can't use replace on a sequence of elements, apparently.

I don't want to do this:

Code: Select all

(replace 4 s )
;-> (1 1 2 2 3 3 5 5 3 3 2 2 1 1)
because it loses the non-doubled ones.

Sammo
Posts: 180
Joined: Sat Dec 06, 2003 6:11 pm
Location: Loveland, Colorado USA

Post by Sammo »

If the idea is to completely remove just the pair of 4s, the following works but I don't claim it's "the best way."

Code: Select all

(define (remove-pair L A)
  (reverse (remove-pair-aux (first L) (rest L) A '())) )

(define (remove-pair-aux head tail atom result)
  (cond
    ( (empty? tail)
      (cons head result)
    )
    ( (!= head atom)
      (remove-pair-aux (first tail) (rest tail) atom (cons head result))
    )
    ( (= head (first tail))
      (remove-pair-aux (first (rest tail)) (rest (rest tail)) atom result)
    )
    ( true
      (remove-pair-aux (first tail) (rest tail) atom (cons head result))
    ) ))
Here's the same function expressed with newLisp's implicit indexing notation:

Code: Select all

(define (remove-pair L A)
  (reverse (remove-pair-aux (L 0) (1 L) A '())) )

(define (remove-pair-aux head tail atom result)
  (cond
    ( (empty? tail)
      (cons head result)
    )
    ( (!= head atom)
      (remove-pair-aux (tail 0) (1 tail) atom (cons head result))
    )
    ( (= head (tail 0))
      (remove-pair-aux (tail 1) (2 tail) atom result)
    )
    ( true
      (remove-pair-aux (tail 0) (1 tail) atom (cons head result))
    ) ))
Here's an example:

Code: Select all

(set 's '(1 1 4 2 2 3 3 4 4 5 5 4 4 3 3 4 2 2 1 1))
;-> (1 1 4 2 2 3 3 4 4 5 5 4 4 3 3 4 2 2 1 1)
(remove-pair s 4)
;-> (1 1 4 2 2 3 3 5 5 3 3 4 2 2 1 1)
;; edit 2006-03-26 9:55 a.m. MST to correct one type

Sammo
Posts: 180
Joined: Sat Dec 06, 2003 6:11 pm
Location: Loveland, Colorado USA

Post by Sammo »

Here's a more general solution that allows you to remove adjacent "groups" of atoms (not just groups of two), and that let's you specify that you want to remove groups of N or larger.

We start by writing the function "grouper" to make lists of adjacent identical atoms in list L:

Code: Select all

(define (grouper L)
  (reverse (grouper-aux (first L) (rest L) '())) )

(define (grouper-aux head tail result)
  (cond
    ( (= head nil)
      result
    )
    ( (empty? result)
      (grouper-aux (first tail) (rest tail) (cons (list head) result))
    )
    ( (member head (first result))
      (push head result 0 0)
      (grouper-aux (first tail) (rest tail) result)
    )
    ( true
      (grouper-aux (first tail) (rest tail) (cons (list head) result))
    ) ))
For example:

Code: Select all

(set 's '(1 1 2 2 3 3 4 4 5 5 4 4 4 3 3 3 2 2 2 1 1 1)) 
;-> (1 1 2 2 3 3 4 4 5 5 4 4 4 3 3 3 2 2 2 1 1 1)
(grouper s)
;-> ((1 1) (2 2) (3 3) (4 4) (5 5) (4 4 4) (3 3 3) (2 2 2) (1 1 1))
And now a function to remove from list L groups of atom A of length N or greater:

Code: Select all

(define (remove-groups L A N)
  (let
    ( result '() )
  ;body of let
    (dolist (sublist L)
      (if (or (!= (sublist 0) A) (< (length sublist) (or N 1)))
        (push sublist result -1)) )
  ;return from let
    result ))
Finally, an example that removes 4's of group length 3 or longer:

Code: Select all

(set 's '(1 1 2 2 3 3 4 4 5 5 4 4 4 3 3 3 2 2 2 1 1 1))
(flat (remove-groups (grouper s) 4 3))
;-> (1 1 2 2 3 3 4 4 5 5 5 3 3 3 2 2 2 1 1 1)

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

This looks like a job for 'match'

Code: Select all

>  (set 's '(1 1 4 2 2 3 3 4 4 5 5 4 4 3 3 4 2 2 1 1))

>  (while (match '(* 4 4 *) s) (set 's (apply append (match '(* 4 4 *) s))))
(1 1 4 2 2 3 3 5 5 3 3 4 2 2 1 1)


Lutz

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

... or even shorter faster:

Code: Select all

> (set 's '(1 1 4 2 2 3 3 4 4 5 5 4 4 3 3 4 2 2 1 1))
(1 1 4 2 2 3 3 4 4 5 5 4 4 3 3 4 2 2 1 1)

>  (while (set 'L (match '(* 4 4 *) s)) (set 's (apply append L)))
(1 1 4 2 2 3 3 5 5 3 3 4 2 2 1 1)
> 
Lutz

cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Post by cormullion »

Hey, Sammo, thanks for all the good code! Very useful stuff, and I started to work through it - then Lutz came up with an even better answer!

Locked