Subject: Array algorithms for report spacing
From: richard.levine@canrem.com (Richard Levine)
Date: Wed,  6 Sep 95 10:37:00 -0500
Organization: CRS Online  (Toronto, Ontario)

ARRAY METHODS FOR REPORT SPACING

In a recent message I described a J program (verb) for a hex
dump report in which I raised the issue of how to insert
spacing lines of a report.  The following message is a
clarification that is relevant to both J and APL.

While I hope the comments below are helpful to users of both
J and APL, I would appreciate any feedback re: errors of
fact or interpretation, or comments on the practicality of
the techniques described here.

In the original message I was referring to using an array
algorithm ("array thinking") to insert all spacing lines
between sections of a report, versus doing so in a while
loop.

I wrote at that time:

>> quote
... a while loop prepares the report one section at a time
(each section with "rps" lines), each section separated from
the following section by a blank line.
>> end quote

Can we give array algorithms to compare with the rather
straight-forward while loop solution?  The following notes
describe such algorithms and at the same time I believe they
provide an instructive comparison between APL and J.

ALGORITHMS IN APL

An array algorithm in APL for inserting a specified number
of blank lines after specified positions in an array may be
defined, in the fashion of the following example:

w .is 0 0 1 0 0 1 0 0 1 0

This means to insert w[i] fill characters after the i-th
position in the array; in the above, insert 1 fill character
after the third, sixth, and ninth position.  There is no APL
primitive that I know which takes an argument like this, but
a different argument used with the "expand" function ( \ )
that provides the same result can be computed, as follows:

expandafter 0 0 1 0 0 1 0 0 1 0
1 1 1 0 1 1 1 0 1 1 1 0 1

(expandafter 0 0 1 0 0 1 0 0 1 0)\'abcdefghij'
abc def ghi j

r .is expandafter w
.comment boolean <r> to insert w[i] 0's after i-th position
.comment algorithm is independent of origin 0 or 1
r .is ( .iota ( .rho w)++/w) .epsilon ( .iota .rho w)+(-1)
.drop 0,+\w

The above and all APL expressions in this note are ISO APL
and therefore applicable to all major APL interpreters.

"Expandafter" can be used in many ways.

Example 1

To insert a blank after every n-th position, we write the
following:

n .is 8
(expandafter (.rho x) .rho (-n) .take 1)\x
abcdefgh ij

This technique applies to any array, in particular, the
typical case of a matrix for a report.  However, note the
changes that need to be made to handle "rows".

x .is 10 2 .rho 'aabbccddeeffgghhiijj'
(expandafter (1 .take .rho x) .rho (-n) .take 1) .expand1 x
aa
bb
cc
dd
ee
ff
gg
hh

ii
jj

The above expression now handles matrices as well as
vectors.

Example 2

Consider the sorted vector:

x .is 'abbcccdddd'

One can insert a blank line at the end of every section as
follows:

(expandafter 1 0 1 0 0 1 0 0 0 1)\x
a bb ccc dddd

The boolean vector used above can be computed with a
function "bp" that computes "break points" (a break point
vector is a vector with a 1 at the beginning of every "run",
a "run" being a sequence of identical elements --  I don't
know if "break point" or "run" are the "correct" or
"commonly used" terms.)

.comment beginning of every run
bp x
1 1 0 1 0 0 1 0 0 0

.comment end of every run
1 .rotate bp x
1 0 1 0 0 1 0 0 0 1

(expandafter 1 .rotate bp x)\x
a bb ccc dddd

y .is bp x
.comment compute 'break points' in <x>
.comment 1 0 1 0 0 1 0 0 0 0 = bp 'aabbbccccc'
.comment 1992.3.19.11.48.59
.comment v 1.0 / 18jul83
.comment treat vector as n by 1 matrix (elements are rows)
x .is (2 .take (.rho x), 1 1) .rho x
y .is 1,(-1) .drop .or / x .ne 1 .rotate1 x

ALGORITHMS IN J

Currently there is interest in how APL translates into J.
"Array thinking" is certainly the same.  However, we may
need to make changes in the syntax and semantics of
expressions and in the data structures.  In these examples
only a few changes are required: in particular changes in
the procedures for function definition, and in a few
primitive functions, such as replacing APL "expand" with the
J "copy" verb.

Since the closest verb in J to APL "expand" seems to be the
verb "copy" ( dyadic # ), we focus our efforts on preparing
an argument for "copy".  ("Copy" seems to be a combination
of the APL functions "compress", "replicate", and "expand".)

x =. 'abcdefghij'
1 1 1j1 1 1 1j1 1 1 1j1 1 # x
abc def ghi j

The algorithm in J for expandafter is, given a specification
vector as defined in the APL case, to apply the "imaginary"
verb ( monadic j. ) and then add 1.

expandafter =. 3 : '1 + j. y.'  NB. explicit definition
expandafter =. (1&+) @ j.       NB. tacit definition
expandafter =. >: @ j.          NB. another tacit definition

expandafter 0 0 1 0 0 1 0 0 1 0
1 1 1j1 1 1 1j1 1 1 1j1 1

(expandafter 0 0 1 0 0 1 0 0 1 0) # x
abc def ghi j

The expression above inserted a blank space after every 3-rd
position in the list.  Following the method described for
APL, this can be written as follows:

NB. (#x) is the number of ITEMS in x
(expandafter (#x)$0 0 1) # x
abc def ghi j

Similarly for every n-th position.
take =. {.
n =. 8
(expandafter (#x)$(-n) take 1)\x
abcdefgh ij

One nice feature about J is the concept of items.  No
changes are required when x is a table (matrix), since #
operates on the ITEMS of an array.  For lists (vectors),
blank elements are inserted between elements; for tables
(matrices), blank rows are inserted between rows, and so on.

The technique using break points applies similarly.

x .is 'abbcccdddd'
(expandafter 1 0 1 0 0 1 0 0 0 1) # x
a bb ccc dddd

bp =. 3 : 0
NB. break points of vector y.
NB. 1 0 0 1 0 0 0 1 1 0 = bp 2 2 2 3 3 3 3 5 4 4
1, 2 ~: /\ y.
)

NB. compute the beginning of every run
bp x
1 1 0 1 0 0 1 0 0 0

NB. compute the end of every run  (rotate =. |.)
1 |. bp x
1 0 1 0 0 1 0 0 0 1

(expandafter 1 |. bp x) # x
a bb ccc dddd

NOTES

Note 1:
For both APL and J versions of expandafter, the argument
need not be boolean.
E.g. w =. 0 0 2 0 2 0 specifies to insert 2 fill elements
after the third and fifth positions.

Note 2:
To ensure that there are no trailing blank lines in reports,
we need to adjust the argument.  E.g. In the case of J, in
the following expression:
(expandafter (#x)$0 0 1) # x
we provide a modified or "amended" value as follows:
(expandafter (0) _1 } (#x)$0 0 1) # x
Note in passing the nice definition of "amend" which returns
the amended result.  This is preferable to the more
complicated method required in APL, namely the following:
(expandafter ( ((-1) .drop ( .rho x) .rho 0 0 1),0) \ x
(This could also be used in J, of course.)

Note 3:
In APL, one can define a function "expandbefore" to prepare
an argument for "expand" that inserts fill elements BEFORE
corresponding elements of an array.  This is a minor
modification to "expandafter".  For J, I have not found a
similar simple modification to the J version of
"expandafter".  (The copy verb inserts fill elements AFTER
the corresponding elements.)  However, I conjecture that
many apparent uses of "expandbefore" in reports can be
transformed into uses of "expandafter".

Note 4:
In my opinion the APL definition of "expandafter" is no
easier to understand than a direct translation into J, or an
equivalent J tacit definition. (Commentary on algorithm has
not been included in this message.)

CONCLUSIONS

The solution using a while loop and the solution using array
processing provide identical results.  The question of which
one to choose depends on the situation, based on programming
simplicity and performance.

APL and J provide similar and elegant solutions.  Taking
into account the relative unfamiliarity of the J language
over APL, the J expressions are simpler and more general.

ACKNOWLEDGEMENTS

Thanks to C. Burke and E. Lamm for ideas in private
communications included in this note.  The APL functions and
examples were derived from the Toronto Toolkit of APL
Functions, edited by the author.
