[Date Prev] [Date Next] [Thread Prev] [Thread Next] Date Index Thread Index Search archive:
Date:Mon Aug 15 09:50:08 1993 
Subject:deleting the nth item of a list 
From:"A.Sloman" 
Volume-ID:930815.01 

This is a question about programming style in Pop-11.

One of my students fell over the most obvious way to define a
procedure to remove the N'th element of a list.

He tried using

	delete(list(N), list) -> list

and found that it didn't work in the case where the N'th item in the
list was = to a preceding item.

I wonder what the most elegant solution is? (He doesn't want the
original list changed.)

Here's one using the pop-11 pattern matcher. (It should probably have an
extra check for "index" being a non-zero positive integer) (See HELP
MATCHES for restrictions on pattern variables):

define remove_nth(index,list) -> list;
	lvars index, list;

	vars first,rest;	;;; pattern variables

	index - 1 -> index;		;;; reduce index by 1 for the pattern
							;;; variable restriction

	if list matches [??first: ^index = ??rest] then
		[^^first ^^rest] -> list
	else
		mishap('TOO FEW ITEMS IN LIST', [^index ^list])
	endif

enddefine;

(NB the space between ":" and "^" is essential in the restriction)

	remove_nth(1, [1 2 3 4 5]) =>
    ** [2 3 4 5]
	remove_nth(5, [1 2 3 4 5]) =>
    ** [1 2 3 4]
	remove_nth(3, [1 2 3 4 5]) =>
    ** [1 2 4 5]

This solution works, but many people don't like using the pattern
matcher because of its dependence on non-lexical variables (cured by
LIB FMATCHES) and its garbage collection overhead, e.g. in re-building
the pattern on every invocation, and in searching for a match when there
are segment variables (i.e. using "??").

There are many possible alternative solutions using allbutfirst,
allbutlast, rev, loops, etc. Here's a fairly simple one, that builds
a list of the first N - 1 items then joins it onto the appropriate tail.

define remove_nth(index,list) -> list;
	lvars count, index, list;

	[%
		for count to index - 1 do
			dest(list) -> list
		endfor
	%]
		<> tl(list) -> list

enddefine;

To reduce garbage collection overheads, use nc_<> (non-constructive
concatenation) instead of <>. (Additional error checks are also
desirable.)

Alternatively do the following, using the stack to store the first N - 1
items, which are later removed one by one:

define remove_nth(index,list) -> list;
	lvars count, index, list;

		for count to index do
			dest(list) -> list
		endfor;

		tl(list) -> list;

		for count to index do
			:: list -> list
		endfor
enddefine;


Is there something more elegant? Is it possible to combine elegance in
solving the problem with good error checking?

Should the Pop-11 procedures allbutfirst and allbutlast be complemented
by procedures allfirst and alllast, so that the solution could be

	allfirst(2, list) <> allbutfirst(3, list) -> newlist

	?

I wonder how often people write the equivalent of allfirst or alllast
inline?
Aaron