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
|