In Rexx a variable can contain the name of another variable. The documentation I have seen gives examples, but doesn't explain why you would want to do that.
Say you want to keep a llst of names and their attributes. In another language you would create an array of names and attributes and search it for the record you want. You could do that in Rexx, but there is an easier way. You can create a separate variable for each name and then simply refer to that variable, without needing to search through a list. Here is an example:
/**/
/* create some variables */
Data = 'Crocus 5 blue Tulip 8 red Daffodil 12 yellow'
NameList = ''
do while Data \= ''
parse var Data !Name Ht Clr Data
NameList = NameList !Name
call value !Name'.Height', Ht
call value !Name'.Color', Clr
end
do w = 1 to words(NameList)
!Name = word(NameList, w)
say !Name':'
say ' Height:' value(!Name'.Height')
say ' Color: ' value(!Name'.Color')
end w
exit
What this does is create stem variables called CROCUS.HEIGHT, CROCUS.COLOR,
TULIP.HEIGHT, TULIP.COLOR, etc. There are some things to note:
* I make a practice of using '!' in the !Name variable to
indicate that it stands for another variable. It makes it easier
for me to understand what is going on.
* you must keep a separate list of the names. That 'NameList' variable is the
only record of what variables you have created.
* use "call Value" to assign the variables. The Value function resolves the
string to a variable name, uppercases the name, and then assigns a value to
it. Likewise use Value() when reading the variable.
But, you ask, what if I want to share those variables with a procedure?
How can I expose the list of variables if I don't know what variables
will exist at the time the script is executed?
You can do that by packaging everything in a stem variable. So the variables
will be called FLOWER.CROCUS.HEIGHT, etc. Then expose the name of the stem.
Like this:
/**/
Data = 'Crocus 5 blue Tulip 8 red Daffodil 12 yellow'
Flower.NameList = ''
do while Data \= ''
parse var Data !Name Ht Clr Data
Flower.NameList = Flower.NameList !Name
call value 'Flower.'!Name'.Height', Ht
call value 'Flower.'!Name'.Color', Clr
end
call ShowAll
exit
ShowAll: procedure expose Flower.
do w = 1 to words(Flower.NameList)
!Name = word(Flower.NameList, w)
say !Name':'
say ' Height:' value('Flower.'!Name'.Height')
say ' Color: ' value('Flower.'!Name'.Color')
end w
return
And now you may be asking, Why use Value()? It is easier to just type
Flower.!Name.Height = Ht
say Flower.!Name.Height
It is easier to type and read, and I often do that myself. You just need to
be careful about the case of the variable name. In this:
!Name = 'Crocus'
Flower.!Name.Height = Ht
Rexx does not create the variable FLOWER.CROCUS.HEIGHT, it creates
FLOWER.Crocus.HEIGHT. So to display that variable with
say Flower.!Name.Height
you must make sure that !Name is 'Crocus', not 'CROCUS' or 'crocus'.
What I do is always make !Name upper-case, as in
!Name = translate('Crocus')
This is the only time I know of when Rexx variable names are case-sensitive. I
don't suppose it is really a bug, but maybe an oversight in the original design
of the Rexx language. Regina Rexx does the same thing.
Run the following script to demonstrate that Rexx can have several variable
names that differ only in case:
/**/
if RxFuncQuery('SysLoadFuncs') then do
call RxFuncAdd 'SysLoadFuncs', 'REXXUTIL', 'SysLoadFuncs'
call SysLoadFuncs
end
!Name = 'Crocus'
call value 'Flower.'!Name'.Height', '"Crocus" using Value()'
Flower.!Name.Height = '"Crocus" without using Value()'
!Name = 'CrOcUs'
Flower.!Name.Height = '"CrOcUs" without using Value()'
drop !Name result
say 'the list of known variables is:'
call SysDumpVariables
exit
This may be more advanced than what Alfredo had in mind, but I felt like sharing it.