lists.icn: Procedures to manipulate lists

procedure file2lst:        create list from lines in file
procedure imag2lst:        convert limage() output to list
procedure l_Bscan:         begin list scanning
procedure l_Escan:         end list scanning
procedure l_any:           any() for list scanning
procedure l_bal:           bal() for list scanning
procedure l_find:          find() for list scanning
procedure l_many:          many() for list scanning
procedure l_match:         match() for list scanning
procedure l_move:          move() for list scanning
procedure l_pos:           pos() for list scanning
procedure l_tab:           tab() for list scanning
procedure l_upto:          upto() for list scanning
procedure llayer:          interleave lists with layering
procedure lcompact:        compact sequence
procedure lclose:          close open palindrome
procedure lcomb:           list combinations
procedure ldecollate:      list decollation
procedure ldelete:         delete specified list elements
procedure ldupl:           list term duplication
procedure lequiv:          compare lists for equivalence
procedure levate:          elevate values
procedure lextend:         list extension
procedure lfliph:          list horizontal flip (reversal)
procedure lflipv:          list vertical flip
procedure limage:          list image
procedure lcollate:        generalized list collation
procedure lconstant:       test list for all terms equal
procedure lindex:          generate indices for items matching x
procedure linterl:         list interleaving
procedure llpad:           list padding at left
procedure lrunup:          list run up
procedure lrundown:        list run up
procedure lltrim:          list left trimming
procedure lmap:            list mapping
procedure lresidue:        list residue
procedure lpalin:          list palindrome
procedure lpermute:        list permutations
procedure lreflect:        list reflection
procedure lremvals:        remove values from list
procedure lrepl:           list replication
procedure lreverse:        list reverse
procedure lrotate:         list rotation
procedure lrpad:           list right padding
procedure lrtrim:          list right trimming
procedure lshift:          shift list terms
procedure lst2str:         convert list to string
procedure lswap:           list element swap
procedure lunique:         keep only unique list elements
procedure lmaxlen:         size of largest list entry
procedure lminlen:         size of smallest list entry
procedure sortkeys:        extract keys from sorted list
procedure sortvalues:      extract values from sorted list
procedure str2lst:         list from string

link lists
March 5, 2003; Ralph E. Griswold
Contributor: Richard L. Goerwitz
This file is in the public domain.

file2lst(s)     create list from lines in file

imag2lst(s)     convert limage() output to list

l_Bscan(e1)     begin list scanning

l_Escan(l_OuterEnvir, e2)
                end list scanning

l_any(l1,l2,i,j)
                any() for list scanning

l_bal(l1,l2,l3,l,i,j
                bal() for list scanning

l_find(l1,l2,i,j)
                find() for list scanning

l_many(l1,l2,i,j)
                many() for list scanning

l_match(l1,l2,i,j)
                match() for list scanning

l_move(i)       move() for list scanning

l_pos(i)        pos() for list scanning

l_tab(i)        tab() for list scanning

l_upto(l1,l2,i,j)
                upto() for list scanning

lclose(L)       close open palindrome

lcomb(L,i)      list combinations

lcompact(L)     compact list, mapping out missing values

ldecollate(I, L)
                list decollation

ldelete(L, spec)
                list deletion

ldupl(L, i)     list term duplication

lequiv(L1, L2)  list equivalence

levate(L, m, n) list elevation

lextend(L, i)   list extension

lfliph(L)       list horizontal flip (reversal)

lflipv(L)       list vertical flip

limage(L)       unadorned list image

lindex(L, x)
                generate indices of L whose values are x

lcollate(L1, L2, ...)
                list collation; like linterl() except stops on
                short list

lconstant(L)    succeeds and returns element if all are the same

linterl(L1, L2) list interleaving

llayer(L1, L2, ...)
                layer and interleave L1, L2, ...

llpad(L, i, x)  list padding at left

lltrim(L, S)    list left trimming

lmap(L1,L2,L3)  list mapping

lpalin(L, x)    list palindrome

lpermute(L)     list permutations

lreflect(L, i)  returns L concatenated with its reversal to produce
                palindrome; the values of i determine "end
                conditions" for the reversal:

                        0       omit first and last elements; default
                        1       omit first element
                        2       omit last element
                        3       don't omit element

lremvals(L, x1, x2, ...)
                remove values from list

lrepl(L, i)     list replication

lresidue(L, m, i)
                list residue

lreverse(L)     list reverse

lrotate(L, i)   list rotation

lrpad(L, i, x)  list right padding

lrundown(L1, L2, L3)
                list run down

lrunup(L1, L2, L3)
                list run up

lrtrim(L, S)    list right trimming

lshift(L, i)    shift list terms

lst2str(L)      string from concatenated values in L

lswap(L)        list element swap

lunique(L)      keep only unique list elements

lmaxlen(L, p)   returns the size of the largest value in L.
                If p is given, it is applied to each string as
                as a "length" procedure.  The default for p is
                proc("*", 1).

lminlen(L, p)   returns the size of the smallest value in L.
                If p is given, it is applied to each string as
                as a "length" procedure.  The default for p is
                proc("*", 1).

sortkeys(L)     returns list of keys from L, where L is the
                result of sorting a table with option 3 or 4.

sortvalues(L)   return list of values from L, where L is the
                result of sorting a table with option 3 or 4.

str2lst(s, i)   creates list with i-character lines from s.  The
                default for i is 1.
____________________________________________________________

             About List Mapping

The procedure lmap(L1,L2,L3) maps elements of L1 according to L2
and L3.  This procedure is the analog for lists of the built-in
string-mapping function map(s1,s2,s3). Elements in L1 that are
the same as elements in L2 are mapped into the corresponding ele-
ments of L3. For example, given the lists

   L1 := [1,2,3,4]
   L2 := [4,3,2,1]
   L3 := ["a","b","c","d"]

then

   lmap(L1,L2,L3)

produces a new list

   ["d","c","b","a"]

   Lists that are mapped can have any kinds of elements. The
operation

   x === y

is used to determine if elements x and y are equivalent.

   All cases in lmap are handled as they are in map, except that
no defaults are provided for omitted arguments. As with map, lmap
can be used for transposition as well as substitution.

Warning:

   If lmap is called with the same lists L2 and L3 as in
the immediately preceding call, the same mapping is performed,
even if the values in L2 and L3 have been changed. This improves
performance, but it may cause unexpected effects.

   This ``caching'' of the mapping table based on L2 and L3
can be easily removed to avoid this potential problem.
____________________________________________________________

     About List Scanning by Richard L. Goerwitz

PURPOSE: String scanning is terrific, but often I am forced to
tokenize and work with lists.  So as to make operations on these
lists as close to corresponding string operations as possible, I've
implemented a series of list analogues to any(), bal(), find(),
many(), match(), move(), pos(), tab(), and upto().  Their names are
just like corresponding string functions, except with a prepended
"l_" (e.g. l_any()).  Functionally, the list routines parallel the
string ones closely, except that in place of strings, l_find and
l_match accept lists as their first argument.  L_any(), l_many(),
and l_upto() all take either sets of lists or lists of lists (e.g.
l_tab(l_upto([["a"],["b"],["j","u","n","k"]])).  Note that l_bal(),
unlike the builtin bal(), has no defaults for the first four
arguments.  This just seemed appropriate, given that no precise
list analogue to &cset, etc. occurs.

The default subject for list scans (analogous to &subject) is
l_SUBJ.  The equivalent of &pos is l_POS.  Naturally, these
variables are both global.  They are used pretty much like &subject
and &pos, except that they are null until a list scanning
expression has been encountered containing a call to l_Bscan() (on
which, see below).

Note that environments cannot be maintained quite as elegantly as
they can be for the builtin string-scanning functions.  One must
use instead a set of nested procedure calls, as explained in the
_Icon Analyst_ 1:6 (June, 1991), p. 1-2.  In particular, one cannot
suspend, return, or otherwise break out of the nested procedure
calls.  They can only be exited via failure.  The names of these
procedures, at least in this implementation, are l_Escan and
l_Bscan.  Here is one example of how they might be invoked:

    suspend l_Escan(l_Bscan(some_list_or_other), {
        l_tab(10 to *l_SUBJ) & {
            if l_any(l1) | l_match(l2) then
                old_l_POS + (l_POS-1)
        }
    })

Note that you cannot do this:

    l_Escan(l_Bscan(some_list_or_other), {
        l_tab(10 to *l_SUBJ) & {
            if l_any(l1) | l_match(l2) then
                suspend old_l_POS + (l_POS-1)
        }
    })

Remember, it's no fair to use suspend within the list scanning
expression.  l_Escan must do all the suspending.  It is perfectly OK,
though, to nest well-behaved list scanning expressions.  And they can
be reliably used to generate a series of results as well.
____________________________________________________________

Here's another simple example of how one might invoke the l_scan
routines:

procedure main()

    l := ["h","e","l","l","o"," ","t","t","t","h","e","r","e"]

    l_Escan(l_Bscan(l), {
        hello_list := l_tab(l_match(["h","e","l","l","o"]))
        every writes(!hello_list)
        write()

        # Note the nested list-scanning expressions.
        l_Escan(l_Bscan(l_tab(0)), {
            l_tab(l_many([[" "],["t"]]) - 1)
            every writes(!l_tab(0))
            write()
        })
    })

end

The above program simply writes "hello" and "there" on successive
lines to the standard output.
____________________________________________________________

PITFALLS: In general, note that we are comparing lists here instead
of strings, so l_find("h", l), for instance, will yield an error
message (use l_find(["h"], l) instead).  The point at which I
expect this nuance will be most confusing will be in cases where
one is looking for lists within lists.  Suppose we have a list,

    l1 := ["junk",[["hello"]," ",["there"]],"!","m","o","r","e","junk"]

and suppose, moreover, that we wish to find the position in l1 at
which the list

    [["hello"]," ",["there"]]

occurs.  If, say, we assign [["hello"]," ",["there"]] to the
variable l2, then our l_find() expression will need to look like

    l_find([l2],l1)
____________________________________________________________

Extending scanning to lists is really very difficult.  What I think
(at least tonight) is that scanning should never have been
restricted to strings.  It should have been designed to operate on
all homogenous one-dimensional arrays (vectors, for you LISPers).
You should be able, in other words, to scan vectors of ints, longs,
characters - any data type that seems useful.  The only question in
my mind is how to represent vectors as literals.  Extending strings
to lists goes beyond the bounds of scanning per-se.  This library is
therefore something of a stab in the dark.

Source code | Program Library Page | Icon Home Page