list utilities (#56)(an instance of Generic Utilities Package made by Hacker)     append (list,list,..) => result of concatenating the given lists      reverse (list) => reversed list      remove_duplicates (list) => list with all duplicates removed      compress (list) => list with consecutive duplicates removed      setremove_all (list,elt) => list with all occurrences of elt removed      find_insert (sortedlist,e) => index of first element > e in sortedlist      sort (list[,keys]) => sorted list      count (elt,list) => count of elt found in list.      flatten (list) => flatten all recursive lists into one list            make (n[,e]) => list of n copies of e      range (m,n) => {m,m+1,...,n}            arrayset (list,val,i[,j,k...]) => array modified so that list[i][j][k]==val            -- Mapping functions (take a list and do something to each element):            map_prop ({o...},prop) => list of o.(prop) for all o      map_verb ({o...},verb[,args) => list of o:(verb)(@args) for all o      map_arg ([n,]obj,verb,{a...},args) => list of obj:(verb)(a,@args) for all a            -- Association list functions --            An association list (alist) is a list of pairs (2-element lists), though the following functions have been generalized for lists of n-tuples (n-element lists). In each case i defaults to 1.            assoc (targ,alist[,i]) => 1st tuple in alist whose i-th element is targ      iassoc (targ,alist[,i]) => index of same.      assoc_prefix (targ,alist[,i]) => ... whose i-th element has targ as a prefix      iassoc_prefix(targ,alist[,i]) => index of same.      slice (alist[,i]) => list of i-th elements      sort_alist (alist[,i]) => alist sorted on i-th elements. VERB SOURCE CODE: make:
":make(n[,elt]) => a list of n elements, each of which == elt. elt defaults to 0.";
if ((n = args[1]) < 0)
return E_INVARG;
endif
ret = {};
build = {elt = {@args, 0}[2]};
while (1)
if (n % 2)
ret = {@ret, @build};
endif
if (n = n / 2)
build = {@build, @build};
else
return ret;
endif
endwhile
.
range:
":range([m,]n) => {m,m+1,...,n}";
if (listdelete(args, 1))
else
args = {1, @args};
endif
ret = {};
for k in [args[1]..args[2]]
ret = {@ret, k};
endfor
return ret;
.
map_prop*erty:
set_task_perms(caller_perms());
prop = args[2];
if ((len = length(objs = args[1])) > 50)
return {@this:map_prop(objs[1..len / 2], prop), @this:map_prop(objs[(len / 2)
+ 1..len], prop)};
endif
strs = {};
for foo in (objs)
strs = {@strs, foo.(prop)};
endfor
return strs;
.
map_verb:
set_task_perms(caller_perms());
if ((len = length(objs = args[1])) > 50)
return {@this:map_verb(@listset(args, objs[1..len / 2], 1)), @this:map_verb(@listset(args,
objs[(len / 2) + 1..len], 1))};
endif
vrb = args[2];
rest = args[3..length(args)];
strs = {};
for o in (objs)
strs = {@strs, o:(vrb)(@rest)};
endfor
return strs;
.
map_arg:
"map_arg([n,]object,verb,@args) -- assumes the nth element of args is a list, calls
object:verb(@args) with each element of the list substituted in turn, returns the
list of results. n defaults to 1.";
"map_verb_arg(o,v,{a...},a2,a3,a4,a5)={o:v(a,a2,a3,a4,a5),...}";
"map_verb_arg(4,o,v,a1,a2,a3,{a...},a5)={o:v(a1,a2,a3,a,a5),...}";
set_task_perms(caller_perms());
if (n = args[1])
object = args[2];
verb = args[3];
rest = args[4..length(args)];
else
object = n;
n = 1;
verb = args[2];
rest = args[3..length(args)];
endif
results = {};
for a in (rest[n])
results = listappend(results, object:(verb)(@listset(rest, a, n)));
endfor
return results;
.
map_builtin:
":map_builtin(objectlist,func) applies func to each of the objects in turn and returns
the corresponding list of results. This function is mainly here for completeness
-- in the vast majority of situations, a simple for loop is better.";
set_task_perms(caller_perms());
if (!((builtin = args[2]) in {"tostr", "tonum", "toobj", "typeof", "length", "random",
"ctime", "valid", "parent", "children", "properties", "verbs", "is_player", "idle_seconds",
"connected_seconds"}))
return E_INVARG;
endif
objs = args[1];
if (length(objs) > 100)
return {@this:map_builtin(objs[1..l = length(objs) / 2], builtin), @this:map_builtin(objs[l
+ 1..length(objs)], builtin)};
endif
strs = {};
for foo in (objs)
strs = {@strs, eval(tostr("return ", builtin, "(", $string_utils:from_value(foo,
1, -1), ");"))[2]};
endfor
return strs;
.
find_insert:
"find_insert(sortedlist,key) => index of first element in sortedlist > key";
" sortedlist is assumed to be sorted in increasing order and the number returned
is anywhere from 1 to length(sortedlist)+1, inclusive.";
lst = args[1];
key = args[2];
if ((r = length(lst)) < 25)
for l in [1..r]
if (lst[l] > key)
return l;
endif
endfor
return r + 1;
else
l = 1;
while (r >= l)
if (key < lst[i = (r + l) / 2])
r = i - 1;
else
l = i + 1;
endif
endwhile
return l;
endif
.
remove_duplicates:
"remove_duplicates(list) => list as a set, i.e., all repeated elements removed.";
out = {};
for x in (args[1])
out = setadd(out, x);
endfor
return out;
.
arrayset:
"arrayset(list,value,pos1,...,posn) -- returns list modified such that";
" list[pos1][pos2][...][posn] == value";
if (length(args) > 3)
return listset(@listset(args[1..3], this:arrayset(@listset(listdelete(args, 3),
args[1][args[3]], 1)), 2));
"... Rog's entry in the Obfuscated MOO-Code Contest...";
else
return listset(@args);
endif
.
setremove_all:
":setremove_all(set,elt) => set with *all* occurences of elt removed";
set = args[1];
what = args[2];
while (w = what in set)
set = listdelete(set, w);
endwhile
return set;
.
append:
"append({a,b,c},{d,e},{},{f,g,h},...) => {a,b,c,d,e,f,g,h}";
if ((n = length(args)) > 50)
return {@this:append(@args[1..n / 2]), @this:append(@args[(n / 2) + 1..n])};
endif
l = {};
for a in (args)
l = {@l, @a};
endfor
return l;
.
reverse: "reverse(list) => reversed list"; return this:_reverse(@args[1]); . _reverse:
":_reverse(@list) => reversed list";
if ((n = length(args)) > 50)
return {@this:_reverse(@args[(n / 2) + 1..n]), @this:_reverse(@args[1..n / 2])};
endif
l = {};
for a in (args)
l = listinsert(l, a);
endfor
return l;
.
compress:
"compress(list) => list with consecutive repeated elements removed, e.g.,";
"compress({a,b,b,c,b,b,b,d,d,e}) => {a,b,c,b,d,e}";
if (l = args[1])
out = {last = l[1]};
for x in (listdelete(l, 1))
if (x != last)
out = listappend(out, x);
last = x;
endif
endfor
return out;
else
return l;
endif
.
sort:
"sort(list[,keys]) => sorts keys (assumed to be all numbers or strings) and returns
list with the corresponding permutation applied to it. keys defaults to the list
itself.";
"sort({x1,x3,x2},{1,3,2}) => {x1,x2,x3}";
lst = args[1];
unsorted_keys = (use_sorted_lst = length(args) >= 2) ? args[2] | lst;
sorted_lst = sorted_keys = {};
for e in (unsorted_keys)
l = this:find_insert(sorted_keys, e);
sorted_keys = listinsert(sorted_keys, e, l);
if (use_sorted_lst)
sorted_lst = listinsert(sorted_lst, lst[length(sorted_keys)], l);
endif
endfor
return sorted_lst || sorted_keys;
.
sort_suspended:
":sort_suspended(interval,list[,keys]) => sorts keys (assumed to be all numbers or
strings) and returns list with the corresponding permutation applied to it. keys
defaults to the list itself.";
"does suspend(interval) as needed.";
set_task_perms(caller_perms());
interval = args[1];
if (typeof(interval) != NUM)
return E_ARGS;
endif
lst = args[2];
unsorted_keys = (use_sorted_lst = length(args) >= 3) ? args[3] | lst;
sorted_lst = sorted_keys = {};
for e in (unsorted_keys)
l = this:find_insert(sorted_keys, e);
sorted_keys[l..l - 1] = {e};
if (use_sorted_lst)
sorted_lst[l..l - 1] = {lst[length(sorted_keys)]};
endif
$command_utils:suspend_if_needed(interval);
endfor
return sorted_lst || sorted_keys;
.
slice:
"slice(alist[,index]) returns a list of the index-th elements of the elements of
alist, e.g., ";
" slice({{\"z\",1},{\"y\",2},{\"x\",5}},2) => {1,2,5}.";
"index defaults to 1 and may also be a nonempty list, e.g., ";
" slice({{\"z\",1,3},{\"y\",2,4}},{2,1}) => {{1,\"z\"},{2,\"y\"}}";
slice = {};
ind = {@args, 1}[2];
if (typeof(ind) == LIST)
for elt in (args[1])
s = {elt[ind[1]]};
for i in (listdelete(ind, 1))
s = {@s, elt[i]};
endfor
slice = {@slice, s};
endfor
else
for elt in (args[1])
slice = {@slice, elt[ind]};
endfor
endif
return slice;
.
assoc:
"assoc(target,list[,index]) returns the first element of `list' whose own index-th
element is target. Index defaults to 1.";
"returns {} if no such element is found";
target = args[1];
indx = {@args, 1}[3];
for t in (args[2])
if (t[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(t) == LIST) && (length(t) >= indx))
return t;
endif
endif
endfor
return {};
.
iassoc:
"iassoc(target,list[,index]) returns the index of the first element of `list' whose
own index-th element is target. Index defaults to 1.";
"returns 0 if no such element is found.";
target = args[1];
indx = {@args, 1}[3];
i = 1;
for lsti in (args[2])
if (lsti[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(lsti) == LIST) && (length(lsti) >= indx))
return i;
endif
endif
i = i + 1;
endfor
return 0;
.
iassoc_suspended:
"iassoc(target,list[,index]) returns the index of the first element of `list' whose
own index-th element is target. Index defaults to 1.";
"returns 0 if no such element is found.";
"suspends as needed.";
set_task_perms(caller_perms());
target = args[1];
indx = {@args, 1}[3];
i = 1;
for lsti in (args[2])
if (lsti[indx] == target)
"... do this test first since it's the most likely to fail; this needs -d";
if ((typeof(lsti) == LIST) && (length(lsti) >= indx))
return i;
endif
endif
i = i + 1;
$command_utils:suspend_if_needed(1);
endfor
return 0;
.
assoc_prefix:
"assoc_prefix(target,list[,index]) returns the first element of `list' whose own
index-th element has target as a prefix. Index defaults to 1.";
target = args[1];
indx = (length(args) >= 3) ? args[3] | 1;
for t in (args[2])
if ((typeof(t) == LIST) && ((length(t) >= indx) && (index(t[indx], target) ==
1)))
return t;
endif
endfor
return {};
.
iassoc_prefix:
"iassoc_prefix(target,list[,index]) returns the index of the first element of `list'
whose own index-th element has target as a prefix. Index defaults to 1.";
target = args[1];
indx = (length(args) >= 3) ? args[3] | 1;
for i in [1..length(lst = args[2])]
if ((typeof(lsti = lst[i]) == LIST) && ((length(lsti) >= indx) && (index(lsti[indx],
target) == 1)))
return i;
endif
endfor
return 0;
.
iassoc_sorted:
"iassoc_sorted(target,sortedlist[,i]) => index of last element in sortedlist whose
own i-th element is <= target. i defaults to 1.";
" sortedlist is assumed to be sorted in increasing order and the number returned
is anywhere from 0 to length(sortedlist), inclusive.";
target = args[1];
indx = {@args, 1}[3];
if ((r = length(lst = args[2])) < 25)
for l in [1..r]
if (target < lst[l][indx])
return l - 1;
endif
endfor
return r;
else
l = 0;
r = r + 1;
while ((r - 1) > l)
if (target < lst[i = (r + l) / 2][indx])
r = i;
else
l = i;
endif
endwhile
return l;
endif
.
sort_alist:
":sort_alist(alist[,n]) sorts a list of tuples by n-th (1st) element.";
if ((alist_length = length(alist = args[1])) < 25)
"use insertion sort on short lists";
return this:sort(alist, this:slice(@args));
endif
sort_on = {@args, 1}[2];
left_index = alist_length / 2;
right_index = (alist_length + 1) / 2;
left_sublist = this:sort_alist(alist[1..left_index], sort_on);
right_sublist = this:sort_alist(alist[left_index + 1..alist_length], sort_on);
"...";
"... merge ...";
"...";
left_key = left_sublist[left_index][sort_on];
right_key = right_sublist[right_index][sort_on];
if (left_key > right_key)
merged_list = {};
else
"... alist_length >= 25 implies right_index >= 2...";
"... move right_index downward until left_key > right_key...";
r = right_index - 1;
while (left_key <= (right_key = right_sublist[r][sort_on]))
if (r = r - 1)
else
return {@left_sublist, @right_sublist};
endif
endwhile
merged_list = right_sublist[r + 1..right_index];
right_index = r;
endif
while (l = left_index - 1)
"... left_key > right_key ...";
"... move left_index downward until left_key <= right_key...";
while ((left_key = left_sublist[l][sort_on]) > right_key)
if (l = l - 1)
else
return {@right_sublist[1..right_index], @left_sublist[1..left_index],
@merged_list};
endif
endwhile
merged_list[1..0] = left_sublist[l + 1..left_index];
left_index = l;
"... left_key <= right_key ...";
if (r = right_index - 1)
"... move right_index downward until left_key > right_key...";
while (left_key <= (right_key = right_sublist[r][sort_on]))
if (r = r - 1)
else
return {@left_sublist[1..left_index], @right_sublist[1..right_index],
@merged_list};
endif
endwhile
merged_list[1..0] = right_sublist[r + 1..right_index];
right_index = r;
else
return {@left_sublist[1..left_index], right_sublist[1], @merged_list};
endif
endwhile
return {@right_sublist[1..right_index], left_sublist[1], @merged_list};
.
sort_alist_suspended:
"sort_alist_suspended(interval,alist[,n]) sorts a list of tuples by n-th element.
n defaults to 1. Calls suspend(interval) as necessary.";
set_task_perms(caller_perms());
"... so it can be killed...";
interval = args[1];
if ((alist_length = length(alist = args[2])) < 10)
"insertion sort on short lists";
$command_utils:suspend_if_needed(interval);
return this:sort(alist, this:slice(@listdelete(args, 1)));
endif
"variables specially expanded for the anal-retentive";
sort_on = {@args, 1}[3];
left_index = alist_length / 2;
right_index = (alist_length + 1) / 2;
left_sublist = this:sort_alist_suspended(interval, alist[1..left_index], sort_on);
right_sublist = this:sort_alist_suspended(interval, alist[left_index + 1..alist_length],
sort_on);
left_element = left_sublist[left_index];
right_element = right_sublist[right_index];
merged_list = {};
while (1)
$command_utils:suspend_if_needed(interval);
if (left_element[sort_on] > right_element[sort_on])
merged_list = {left_element, @merged_list};
if (left_index = left_index - 1)
left_element = left_sublist[left_index];
else
return {@right_sublist[1..right_index], @merged_list};
endif
else
merged_list = {right_element, @merged_list};
if (right_index = right_index - 1)
right_element = right_sublist[right_index];
else
return {@left_sublist[1..left_index], @merged_list};
endif
endif
endwhile
.
randomly_permute:
":randomly_permute(list) => list with its elements randomly permuted";
" each of the length(list)! possible permutations is equally likely";
plist = {};
for i in [1..length(ulist = args[1])]
plist = listinsert(plist, ulist[i], random(i));
endfor
return plist;
.
count:
"$list_utils:count(item, list)";
"Returns the number of occurrences of item in list.";
if (length(args) != 2)
return E_ARGS;
elseif (typeof(xlist = args[2]) != LIST)
return E_INVARG;
endif
x = args[1];
counter = 0;
for elt in (xlist)
if (x == elt)
counter = counter + 1;
endif
endfor
return counter;
.
flatten:
"Copied from $quinn_utils (#34283):unroll by Quinn (#19845) Mon Mar 8 09:29:03 1993
PST";
":flatten(LIST list_of_lists) => LIST of all lists in given list `flattened'";
newlist = {};
for elm in (args[1])
if (typeof(elm) == LIST)
newlist = {@newlist, @this:flatten(elm)};
else
newlist = {@newlist, elm};
endif
endfor
return newlist;
.
longest shortest: "Copied from APHiD (#33119):longest Sun May 9 21:00:18 1993 PDT"; "$list_utils:longest( check_nonstring_tell_lines:
"check_nonstring_tell_lines(lines)";
if (caller_perms().wizard)
"don't let a nonwizard mess up our stats";
for line in (args[1])
if (typeof(line) != STR)
this.nonstring_tell_lines = listappend(this.nonstring_tell_lines, callers());
return;
endif
endfor
endif
.
PROPERTY DATA:       nonstring_tell_lines |