I'd like to do some sorting of strings/stems in rexx. What's
the best way to do this? I really don't want to install any
packages because the app I'm building already needs
rexx and rexx/dw. I don't want users to have to install
Robert A "Bob" Cruz
One possibility is to write the data to be sorted to an external file, end invoke your system's sorting program (under Windows, its SORT), and read back in the sorted data afterward. This may be the most efficient approach for large volumes of data.
I'll post some notes on internal (Rexx) sorting shortly.
You can implement your favorite sorting algorithm as a subroutine to sort a specific stem, like the following example for "bubble" sort. Note that this routine must be internal, and the lines to be sorted must be placed in the stem "s.".
/*REXX example of how to sort a stem */
s.1 = 'z'
s.2 = 'a'
s.3 = 'm'
s.0 = 3
DO ii = 1 TO s.0
SAY 's.'ii '=' s.ii
/* Simple "bubble" sort */
sort_stem: PROCEDURE EXPOSE s.
DO ii = 1 TO s.0 - 1
DO jj = ii + 1 TO s.0
IF s.ii > s.jj
THEN /* Swap s.ii and s.jj */ DO
temp = s.ii
s.ii = s.jj
s.jj = temp
END /* Swap s.ii and s.jj */
END /* jj = 2 TO s.0 */
END /* ii = 1 TO s.0 - 1 */
/*----------------------END CODE EXAMPLE--------------*/
Note that for this to work, the tails of the stem must be simple unsigned integers without leading zeroes.
While this works, you will find it inconvenient if you wish to sort more than one stem, as you'll either have to duplicate the subroutine for each stem, or copy the stem to be sorted into s. prior to the call and back out afterwards. It would be best if we could pass the stem as a parameter to the sort routine. Two obstacles stand in our way: Rexx does not allow you to pass stems, and parameters in Rexx are passed by value, so that changes made to an argument in a subroutine are not returned to the calling program. Therefore, our only two possible approaches involve open subroutines (where *all* the variables in the calling program are available to the called routine) and a closed subroutine with EXPOSE.
Note that in these examples I assume that <stem_name> includes the trailing period (otherwise it wouldn't be a stem name, would it ;-). If we have the *name* of the stem to be sorted, we can obtain the value of the n-th element by using the function VALUE( stem_name || n ). Here we build a character string which is the compound name of the element in the stem and pass it to the VALUE() function to obtain its value. VALUE can also be used to *assign* a new value to a given identifier, as in:
old_value = VALUE( stem_name || n, new_value )
or, if old_value is not needed:
CALL VALUE stem_name || n, new_value
I generally don't like using open procedures, because it is easy to accidentally use the same name for a local variable as in one of the calling routines, and overwrite it, causing the calling routine to malfunction. However, since we don't know in advance which stem to EXPOSE, we would seem to have little choice. There is a way to minimize the potential for danger, though: use an open subroutine to *only* capture the name of the stem to be sorted into a variable whose name *shouldn't* be used by any calling program. Then the open routine can call a closed routine, and any variables we create in the subroutine (except for the single variable passed from the open subroutine and the stem itself) will be local to the closed subroutine. The following example shows how this is done.
/*REXX example #2 of how to sort a stem */
CALL sort_stem 's.'
/* Sort second stem */
n.1 = 9
n.2 = 5
n.3 = -1
n.4 = 0
n.0 = 4
CALL sort_stem 'n.'
DO ii = 1 TO n.0
SAY 'n.'ii '=' n.ii
sort_stem: /* Open subroutine */
PARSE ARG _sort_stem_name
/* Actual "bubble" sort */
sort_stem_2: PROCEDURE EXPOSE (_sort_stem_name)
nr_elements = VALUE( _sort_stem_name||0 )
DO ii = 1 TO nr_elements - 1
element_ii_name = _sort_stem_name||ii
DO jj = ii + 1 TO nr_elements
element_jj_name = _sort_stem_name||jj
IF VALUE( element_ii_name ) > VALUE( element_jj_name )
THEN /* Swap _sort_stem.ii and _sort_stem.jj */ DO
temp = VALUE( element_ii_name )
CALL VALUE element_ii_name, VALUE( element_jj_name )
CALL VALUE element_jj_name, temp
END /* Swap _sort_stem.ii and _sort_stem.jj */
END /* jj = 2 TO nr_elements */
END /* ii = 1 TO nr_elements - 1 */
/*--------------------------END CODE EXAMPLE ----------*/
The sorting algorithm I used here is extremely simple, and quite slow. I used it because I wanted to discuss aspects of the Rexx language pertaining to passing stems as parameters, not about the
intricacies of good sorting (for that, see any number of the extensive tomes written on the subject, esp. by Knuth and by Flores). Let me suggest, however, that if you have such a volume of data that you are in need of a sophisticated sorting algorithm, then you need to use an external sorting facility.
Final thought: if you are sorting *words* insted of lines, that could be done in a simple function which returned a string of the sorted words.
When posting it is very helpful for responders to know things like the platform on which you use Regina and also the Regina version along with any other details that may be pertinent. bob_cruz has mentioned the OS sorting tool or tools as one resource you can use. Assuming Windows for the moment and SORT.EXE, aside from using an intermediate data file one can employ the ADDRESS WITH to utilize sort.exe more directly. The Regina 3.3 documentation includes this capability and syntax:
ADDRESS SYSTEM "sort" WITH INPUT STEM names. OUTPUT STEM names
Although you stated that no additional external function packages are desired you may still be interested in the RexxUtil.dll (P. Mcphee) Stem functions. ("7.1 List of Stem Manipulation Routines") such as SysStemSort()
What platform is this for?
It is possible to build a Regina executable that has Rexx/DW and Regutil statically linked in so that the only items you would need to distribute were the regina executable and the dwindows DLLs or shared libraries.
Under Windows it would take a small bit of effort to do this, but on Un*x platforms this capability is already available
Robert A "Bob" Cruz
I know this is somewhat off-topic, but I would be interested to learn:
(1) what is Rexx/DW? (and where do I get it?)
(2) how would one bundle Regina and one or more utility libraries into a single package under Windows?