/* MAIN PROGRAM */
globals = 'bign smalln errcnt n x. searchname sortname pqmax'
bign = 12; smalln = 5; errcnt = 0
say "testing assert -- should fail"
call assert 1 = 0
say "testing select"
do n = 0 to bign
say " n=" n
call clearsubs
do i = 1 to n
call geninorder
call select i
call checkselect i
end
do i = 1 to n
call scramble
call select i
call checkselect i
end
call genequal
do i = 1 to n
call select i
call checkselect i
end
call checksubs
end
say "testing quick sort"
call testsort "qsort"
say "testing insertion sort"
call testsort "isort"
say "testing heap sort"
call testsort "hsort"
say "testing priority queues"
do m = 0 to bign
say " m=" m
call clearsubs
call pqinit m
do i = 1 to m
call pqinsert i
end
do i = m to 1 by -1
call assert pqextractmax() = i
end
call assert n = 0
call pqinit m
do i = m to 1 by -1
call pqinsert i
end
do i = m to 1 by -1
call assert pqextractmax() = i
end
call assert n = 0
call pqinit m
do i = 1 to m
call pqinsert 1
end
do i = m to 1 by -1
call assert pqextractmax() = 1
end
call assert n = 0
n = m
call checksubs
end
say "testing sequential search"
call testsearch "ssearch"
say "testing binary search"
call testsearch "bsearch"
say "total errors (1 expected):" errcnt
if errcnt > 1 then
say ">>>> TEST FAILED <<<<"
exit
/* TESTING ROUTINES */
assert: procedure expose (globals)
parse arg cond
if \cond then do
errcnt = errcnt + 1
say " >> assert failed <<"
end
return
checkselect: procedure expose (globals)
parse arg k
do i = 1 to k - 1
call assert x.i <= x.k
end
do i = k + 1 to n
call assert x.i >= x.k
end
return
checksort: procedure expose (globals)
do i = 1 to n - 1
iplus1 = i + 1
call assert x.i <= x.iplus1
end
return
checksubs: procedure expose (globals)
do i = 1 to n
drop x.i
end
/* assert x. is empty; not possible in rexx */
return
clearsubs: procedure expose (globals)
drop x.
return
genequal: procedure expose (globals)
do i = 1 to n
x.i = 1
end
return
geninorder: procedure expose (globals)
do i = 1 to n
x.i = i
end
return
scramble: procedure expose (globals)
do i = 1 to n
call swap i, random(i, n)
end
return
search: procedure expose (globals)
parse arg t
select
when searchname = "bsearch" then return bsearch(t)
when searchname = "ssearch" then return ssearch(t)
otherwise do
say "invalid search name"
call assert 1 = 0
return
end
end
sort: procedure expose (globals)
select
when sortname = "qsort" then call qsort 1, n
when sortname = "hsort" then call hsort
when sortname = "isort" then call isort
otherwise do
say "invalid sort name"
call assert 1 = 0
end
end
return
testsearch: procedure expose (globals)
parse arg searchname
do n = 0 to bign
say " n=" n
call clearsubs
call geninorder
do i = 1 to n
call assert search(i ) = i
call assert search(i - 0.5) = 0
call assert search(i + 0.5) = 0
end
call genequal
call assert search(0.5) = 0
if n > 0 then
call assert search(1) >= 1
call assert search(1) <= n
call assert search(1.5) = 0
call checksubs
end
return
testsort: procedure expose (globals)
parse arg sortname
say " pathological tests"
do n = 0 to bign
say " n=" n
call clearsubs
call geninorder
call sort
call checksort
do i = 1 to n/2
call swap i, n+1-i
end
call sort
call checksort
call genequal
call sort
call checksort
call checksubs
end
say " random tests"
nfac = 1
do n = 1 to smalln
say " n=" n
nfac = nfac * n
call clearsubs
call geninorder
do i = 1 to nfac
call scramble
call sort
call checksort
end
call checksubs
end
return
/* SET ALGORITHMS */
/* swap -- exchange x.i and x.j */
swap: procedure expose (globals)
parse arg i, j
t = x.i; x.i = x.j; x.j = t
return
/* select -- partition x. so k'th largest element is in x.k */
/* average time complexity of n */
/* worst-case time complexity of n * n if x.1 = ... = x.n */
select: procedure expose (globals)
parse arg k
l = 1
u = n
do while l < u
call swap l, random(l, u)
t = x.l
m = l
do i = l + 1 to u
if x.i < t then do
m = m + 1
call swap m, i
end
end
call swap l, m
if m <= k then l = m + 1
if m >= k then u = m - 1
end
return
/* qsort -- quick sort of x.l to x.u */
/* recursive; call initially by qsort(1,n) */
/* average time complexity of n log n */
/* worst-case time complexity of n * n if x.1 = ... = x.n */
qsort: procedure expose (globals)
parse arg l, u
if l < u then do
call swap l, random(l, u)
t = x.l
m = l
do i = l + 1 to u
if x.i < t then do
m = m + 1
call swap m, i
end
end
call swap l, m
call qsort l, m - 1
call qsort m + 1, u
end
return
/* isort -- insertion sort of x.1 to x.n */
/* average time complexity of n * n */
/* best-case time complexity of n when x.1 <= ... <= x.n */
isort: procedure expose (globals)
do i = 2 to n
do j = i to 2 by -1
jminus1 = j - 1
if x.jminus1 > x.j then
call swap jminus1, j
else
leave
end
end
return
/* siftup -- re-establish heap after adding element to end */
/* time complexity of log n */
siftup: procedure expose (globals)
parse arg l, u
i = u
do forever
if i <= l then leave
p = trunc(i / 2)
if x.p >= x.i then leave
call swap p, i
i = p
end
return
/* siftdown -- re-establish heap after extracting maximum */
/* time complexity of log n */
siftdown: procedure expose (globals)
parse arg l, u
i = l
do forever
c = 2 * i
if c > u then leave
cplus1 = c + 1
if cplus1 <= u then
if x.cplus1 > x.c then
c = cplus1
if x.i >= x.c then leave
call swap c, i
i = c
end
return
/* hsort -- heap sort of x.1 to x.n */
/* time complexity of n log n */
hsort: procedure expose (globals)
do i = trunc(n / 2) to 1 by -1
call siftdown i, n
end
do i = n to 2 by -1
call swap 1, i
call siftdown 1, i - 1
end
return
/* pqinit -- initialize priority queue */
/* time complexity of 1 */
pqinit: procedure expose (globals)
parse arg pqmax
n = 0
return
/* pqinsert -- insert t into priority queue */
/* time complexity of log n */
pqinsert: procedure expose (globals)
parse arg t
call assert n < pqmax
n = n + 1
x.n = t
call siftup 1, n
return
/* pqextractmax -- delete and return value of */
/* maximum element in priority queue */
/* assumes priority queue is non-empty */
/* time complexity of log n */
pqextractmax: procedure expose (globals)
call assert n >= 1
t = x.1
x.1 = x.n
n = n - 1
call siftdown 1, n
return t
/* ssearch -- sequential search for t in x.1 to x.n */
/* returns position of t in x, or 0 if t not in x */
/* returns first of multiple identical items */
/* time complexity of n */
ssearch: procedure expose (globals)
parse arg t
do i = 1 to n
if x.i = t then return i
end
return 0
/* bsearch -- binary search for t in sorted x.1 to x.n */
/* assumes x.1 <= x.2 <= x.i <= x.n for all 1 <= i <= n */
/* returns position of t in x, or 0 if t not in x */
/* returns any of multiple identical items */
/* time complexity of log n */
bsearch: procedure expose (globals)
parse arg t
l = 1; u = n
do while l <= u
m = trunc((l + u) / 2)
select
when x.m < t then l = m + 1
when x.m > t then u = m - 1
otherwise /* x.m = t */ return m
end
end
return 0
--
Phil Bewig ... pbe...@netcom.COM
Nice review, especially for folks not already familiar with REXX.
A few notes:
Many of wishes - call by reference, computed tail values, etc. have been
expressed by IBMers for years. There is work going on to architect this
stuff out, but who can say when it will appear in a product (and don't
ask me, because I am in NO way involved).
wrt: pass by reference. REXX level 4.0 includes kind-of-a way to call
by reference. You can do a "procedure expose (varList)" after a label.
The value of varList is used as a list of variable names to expose.
Below is a sample of it's use. Obviously not all that great (you have
to set the 'varList' variable before calling the function). In my example,
I hide this by invoking a 'shadow' function.
====================================================================
/*------------------------------------------------------------------
* arg.cmd :
*------------------------------------------------------------------
* 08-23-93 originally by Patrick J. Mueller
*------------------------------------------------------------------*/
a.0 = 3
do i = 1 to a.0
a.i = i * i
end
rc = copyArray("a.","b.")
do i = 1 to b.0
say b.i
end
rc = copyArray("b.","c.")
do i = 1 to c.0
say c.i
end
exit
copyArray:
arrays = arg(1) arg(2)
return copyArray2()
copyArray2: procedure expose (arrays)
sArray = word(arrays,1)
dArray = word(arrays,2)
rc = value(dArray"0",value(sArray"0"))
do i = 1 to value(sArray"0")
rc = value(dArray"i",value(sArray"i"))
end
return ""
====================================================================
I've also seen a neat trick for computed tail values. It involves
creating a function with the same name as the stem variable, including
the "." suffix. Here's an example:
====================================================================
/*------------------------------------------------------------------
* inc.cmd :
*------------------------------------------------------------------
* 08-23-93 originally by Patrick J. Mueller
*------------------------------------------------------------------*/
/*------------------------------------------------------------------
* easier incremental list
*------------------------------------------------------------------*/
list.0 = 0
do i = 1 to 3
list.0 = list.0 + 1
rc = list.(list.0,i*i)
end
do i = 1 to list.0
say list.i
end
/*------------------------------------------------------------------
* compute fibonacci numbers
*------------------------------------------------------------------*/
list.1 = 1
list.2 = 1
do i = 3 to 10
list.i = list.(i-1) + list.(i-2)
end
do i = 1 to 10
say list.i
end
exit
/*------------------------------------------------------------------
* function to access and set stemmed variable with computed index
*------------------------------------------------------------------*/
list. : procedure expose list.
index = arg(1)
value = arg(2)
if (arg() = 1) then
return list.index
list.index = value
return value
====================================================================
Patrick Mueller addr: pmu...@vnet.ibm.com (node carvm3)
IBM PRGS Cary, North Carolina phone: 919-469-7242 (tie-line 883)
function_name(arg_list)=expression
causes invocation of function_name||'PV'
with the argument list VALUE(expression),arg_list
Now any function may also operate as a pseudo-variable if it wishes to,
and the implementation doesn't need to worry about what a pseudo-variable
is. Hmmmm.
Dave Gomberg, role model for those who don't ask much in their fantasy lives.
GOMBERG@UCSFVM Internet node UCSFVM.UCSF.EDU fax-> (415)731-7797
I agree this is a pain sometimes (which is why I allowed it in REXX/imc).
> There are no records or structs, so a programmer
>must resort to the old fortran trick of parallel arrays.
I'm not entirely sure what you mean by "no records or structs", but what
you can do is write
book.1._author = "M. F. Cowlishaw"
book.1._title = "The REXX Language, a practical approach to programming"
book.2._author = "A. S. Rudd"
book.2._title = "Practical Usage of REXX"
<etc>
say book.i._title "by" book.i._author
It looks like a struct to me. :-) [there's nothing special about the
underscore characters, but that's one way of ensuring that the variables
_author and _title do not get re-used. Another way is to make them into
constant symbols such as 1author and 1title].
>All elements in a conditional expression are evaluated,
>preventing the "short-circuit" evaluation of conditions as
>in C;
This is occasionally a problem for me. Sometimes you can get around it by
replacing "&" with "then if". If the thing on the right-hand side of an "|"
is really slow, then you could write "or('a','b')" instead of "a|b" where
or: parse arg a,b
interpret "if" a "then return 1"
interpret "return" b
but that's rather a hack. :-)
> in most other languages it
>would be possible to make the name of the compound variable
>to be sorted a parameter to the sort procedure.
REXX/imc allows:
parse arg stemname
procedure expose (stemname)
although getting and assigning the values of this stem is rather difficult,
needing either an INTERPRET or a call to VALUE. Note that the argument must
include the final dot of the stem name.
Uni-REXX and Regina allow:
interpret 'procedure expose' arg(1)
Neither of these is "correct" behaviour, because the ruling on "procedure"
is that it should be the first instruction executed on entry to a subroutine
(and the "interpret" is counted). This rule did not always exist, and
personally I think that there are valid reasons for relaxing it, the above
being one of them. Adding extra code just to try and prevent this is not
high on my list of priorities (though interestingly, REXX/imc does not
allow the interpret trick because the interpeted string is regarded as
being in a separate level).
>There are no static variables, so any variable which is
>local to a procedure but must retain its value between calls
>of the procedure must be global.
> Any variable which
>is to be initialized must be global, because otherwise its
>scope is only the procedure in which it is initialized.
This is one of the things argued in a recent thread on "software
engineering". Basically, we agree with you...
>One problem which sounds simple but has the capacity to do
>great harm is the lack of any syntactic limits on a
>procedure.
I don't see precisely whay you mean by "syntactic limits". Accidental
fall-through is a problem in some other languages, whereas in others you
are forced to end the procedure with an END or a brace. If you want,
you can type "return" at the end of each procedure as a substitute for
this END or brace. Perhaps you wanted the interpreter to enforce such
practice. I don't think this is realistic given the current syntax.
Historically speaking, it is also a problem that the interpreter may
not read in the entire program before executing it, and so it would
never be able to discover the missing brace or whatever.
> consider this scenario: during
>program development or maintenance, a procedure which is a
>large "if ... else if ... else" chain, with each alternative
>in the chain processing its own return, is modified so that
>none of the alternatives is selected,
Ah well, if you had used the correct construction (namely "select") in the
first place, this wouldn't have happened. Unlike an if-elseif chain, a
select is not allowed to fall through without executing any instructions.
>Although a program can create an arbitrary number of tails
>for a single compound variable, Rexx makes no provision to
>iterate over all of the tails, as in the "for (i in x)" loop
>of awk.
Indeed, this has been seen here in comp.lang.rexx several times.
Ian Collier
Ian.C...@prg.ox.ac.uk | i...@ecs.ox.ac.uk
>function_name(arg_list)=expression
>causes invocation of function_name||'PV'
>with the argument list VALUE(expression),arg_list
Excuse me if I missed something, but why don't you just write this?:
call function_namepv expression,arg_list
It will save a lot of work implementing a marginally useful new feature.
BTW your "value" above is not the same as the built-in function VALUE()
because the latter only does variables (not whole expressions). You
should try to pick unique names for your functions. :-)
Ian Collier
Ian.C...@prg.ox.ac.uk | i...@ecs.ox.ac.uk
Because I wish to permit x.(I+1) = x.I + x.(I-1)
>BTW your "value" above is not the same as the built-in function VALUE()
>because the latter only does variables (not whole expressions). You
>should try to pick unique names for your functions. :-)
It is the same as CMS VALUE(), which does permit expressions according
to the help file.
You also get SUBSTR(A,5,3)="ABC" with only that tiny language change.
>In article <pbewigCC...@netcom.com>, pbe...@netcom.com (Phil Bewig) wrote:
>>All elements in a conditional expression are evaluated,
>>preventing the "short-circuit" evaluation of conditions as
>>in C;
>
> you could write "or('a','b')" instead of "a|b" where
> or: parse arg a,b
> interpret "if" a "then return 1"
> interpret "return" b
Or:
or: interpret "if" arg(1) "then return 1"
interpret "return" arg(2)
This way you avoid using variables.
Horst
- - - - - - - - - - - - - - - - - - - - - - -
Horst Kiehl - Internet h.p....@kfa-juelich.de
>Because I wish to permit x.(I+1) = x.I + x.(I-1)
>You also get SUBSTR(A,5,3)="ABC" with only that tiny language change.
Hmmm, I'm still not exactly happy with it, though I can't think of any real
objections at the moment. There is one small one, though: the names of your
functions will have to be restricted to avoid ambiguities like
say(6) = 7
which is at odds with the REXX philosophy of no reserved words.
>>BTW your "value" above is not the same as the built-in function VALUE()
>It is the same as CMS VALUE(), which does permit expressions according
>to the help file.
Are you *sure*? I seem to remember (years ago) thinking that it did and
finding that it didn't. Note that it's OK for the help file to say:
value(expression)
The expression is evaluated and...
because that's normal for a function call. What you want is for the
expression to result in an expression which will be evaluated, and I
still don't believe that it will be. The expression should result in
a symbol name whose value will be returned.
If you still think the help file allows value() to evaluate expressions,
try it out. So far, three interpreters (and the draft ANSI standard) agree
with me and none with you... ;-)
Ian Collier
Ian.C...@prg.ox.ac.uk | i...@ecs.ox.ac.uk
As I see it right now your example is an error (since say(6) has the form
of a function reference which cannot appear on the LHS), but it works in
CMS as SAY (6)=7. I think it should be an error now, and in the expanded
definition, it should be a function invocation.
>>>BTW your "value" above is not the same as the built-in function VALUE()
>
>>It is the same as CMS VALUE(), which does permit expressions according
>>to the help file.
>
>Are you *sure*? I seem to remember (years ago) thinking that it did and
>finding that it didn't. Note that it's OK for the help file to say:
Well it works now. I tried it. abc=1;say value('a'||'b'||'c')
says 1.
>If you still think the help file allows value() to evaluate expressions,
>try it out. So far, three interpreters (and the draft ANSI standard) agree
>with me and none with you... ;-)
Now it's 3:1. Dave
>Ian.C...@prg.ox.ac.uk | i...@ecs.ox.ac.uk
I'm curious as to the reason for not "short-circuiting" the
evaluation.
>Or:
> or: interpret "if" arg(1) "then return 1"
> interpret "return" arg(2)
>This way you avoid using variables.
IMHO the whole idea of INTERPRET *plus* the invocation of a function
doesn't seem exactly cheap resource-wise; "a" must be very 'cheap'
and "b" quite 'expensive' to offset the 'costs'... (?)
The following provides for more than just "a|b", and simply skips
unspecified or blank-only arguments.
/* */
Or: Procedure
Do I=1 to arg()
If strip(arg(I))='' then iterate
Interpret "If" arg(I) "then return 1"
End I
Return 0
Regards.
$$\
"We now present the conclusion of ... The Never-ending Story"
This would be nice and in keeping with REXX's Fourth (Fifth?) generation flavor.
However, the following would produce:
> say(6) = 7
0 meaning False.
>which is at odds with the REXX philosophy of no reserved words.
I have heard this many times, but I don't see what reserved words
(or non-reserved words) have to do with it: the intention of say(6)=7 is
ambiguous. If you reserved the word "say" then you could not
use it for any other purpose, which would be contrary to REXX philosophy,
but so would trying to make a function named "say" wouldn't it?
>>It is the same as CMS VALUE(), which does permit expressions according
>Are you *sure*? I seem to remember (years ago) thinking that it did and
>finding that it didn't. Note that it's OK for the help file to say:
>If you still think the help file allows value() to evaluate expressions,
>try it out. So far, three interpreters (and the draft ANSI standard) agree
>with me and none with you... ;-)
say value(7>6)
returns 1(This is under CMS/REXX v.5, so not a new feature).
Is this what you mean by an expression?
--
Rodney Sparapani Great Multi-word Names for new Seattle bands
Trilogy Consulting, Corp. Right Wing Death Squad Pontius Pilot
courtesy of Monsanto Agricultural Co. Peter, the Love Puppet
This would only be true if we fail to require that an instruction name meant
to be interpreted as an instance of that instruc tion be followed by a blank.
Such a restriction seems eminently sensible to me. Does anyone really want
to write PARSEVAR meaning to have a parse instruction of the VAR persuasion?
>>which is at odds with the REXX philosophy of no reserved words.
What philosophy of no reserved words? Try DO WHILE=3; SAY Hi; END
sometime, for a real thrill. There are three possible interpretations, and
the one I get in CMS I would call the least likely.
} In article <1993Aug24.1...@msc7.comlab.ox.ac.uk>,
} i...@comlab.ox.ac.uk (Ian Collier) writes:
}
} >In article <pbewigCC...@netcom.com>, pbe...@netcom.com (Phil Bewig) wrote:
}
} >>All elements in a conditional expression are evaluated,
} >>preventing the "short-circuit" evaluation of conditions as
} >>in C;
} >
} > you could write "or('a','b')" instead of "a|b" where
} > or: parse arg a,b
} > interpret "if" a "then return 1"
} > interpret "return" b
}
} Or:
}
} or: interpret "if" arg(1) "then return 1"
} interpret "return" arg(2)
On the other hand, if you're worried about efficiency, doing an
'interpret' is *guaranteed* to be a loser [I shudder to think that
folk would seriously put an 'interpret' into a time-critical inner
loop...] It'd be a lot faster to use a little procedure. Instead
of
if a | b then <code>
you can do
if a then call codeproc
else if b then call codeproc
....
codeproc:
<code>
return
/Bernie\
--
Bernie Cosell cos...@world.std.com
Fantasy Farm Fibers, Pearisburg, VA (703) 921-2358
>As I see it right now your example is an error (since say(6) has the form
>of a function reference which cannot appear on the LHS), but it works in
>CMS as SAY (6)=7.
>definition, it should be a function invocation.
There is nothing preventing "function()=value" at present as far as I
can see. It is an expression which yields 0 or 1 and is passed to the
environment. At present my example "say(6)=7" should evaluate "(6)=7"
and say the result.
>>>>BTW your "value" above is not the same as the built-in function VALUE()
>Well it works now. I tried it. abc=1;say value('a'||'b'||'c')
>says 1.
As I explained in detail, this is not a valid use of the function value()
to evaluate an expression. The expression 'a'||'b'||'c' is evaluated before
value() even sees it, so value() itself is only giving the value of the
variable "abc".
Try: expression='1+2'; say value(expression)
This is the context in which you originally wrote "VALUE()", and it is an
incorrect use. Here is your original text:
>function_name(arg_list)=expression
>causes invocation of function_name||'PV'
>with the argument list VALUE(expression),arg_list
imc
In article <1993Aug25.1...@tin.monsanto.com>,
ras...@asdrs1.monsanto.com (Rodney A Sparapani) writes:
>say value(7>6)
>
>returns 1(This is under CMS/REXX v.5, so not a new feature).
That doesn't demonstrate anything, since 7>6 gets evalated by the
REXX interpreter before it gets passed to the VALUE function.
More interesting would be what
say value("7>6")
returns. On TSO/REXX it generates an "Incorrect call to routine"
error because 7>6 is not a valid variable name.
(On the other hand, value(7>6) also ought to return an error,
because 1 is not a valid symbol name. The fact that it doesn't
seems to point up a bug or oversight, unless I'm missing something.)
In Regina, value(7>6) returns 1, and value("7>6") returns 7>6.
At least it's consistent, which is superior to what TSO/REXX does.
--
Steve Bacher (Batchman) Draper Laboratory
Internet: s...@draper.com Cambridge, MA, USA
>This would only be true if we fail to require that an instruction name meant
>to be interpreted as an instance of that instruc tion be followed by a blank.
>Such a restriction seems eminently sensible to me. Does anyone really want
>to write PARSEVAR meaning to have a parse instruction of the VAR persuasion?
No. "parsevar" is a single token, whereas "parse var" is two tokens.
That's not to say that a space is required. It is perfectly possible to
write "if(x==3)then say'hello'", just as it is possible to say "nop;", and
the distinction between tokens is equally clear. It would be too pedantic
to require a space between "if" and "(x==3)", or between "nop" and ";", in
my opinion.
>>>which is at odds with the REXX philosophy of no reserved words.
>What philosophy of no reserved words? Try DO WHILE=3; SAY Hi; END
>sometime, for a real thrill.
The word "while" in this instance is a variable, because it is not a
reserved word. That's the philosophy of no reserved words for you.
(Aside: Uni-REXX doesn't agree...)
In "say(6)=7", there are various alternative interpretations (in a situation
where pseudovariables have been implemented):
1. "say" is not a reserved word because it is not followed by a space.
As I said, I think this borders on the pedantic, and I reject this
option.
2. "say" is not a reserved word because, as we can clearly see, it is the
name of a function which is on the left-hand side of '='. I reject
this option because the lexical analyser which decides whether or not
"say" is a keyword has to do an enormous amount of lookahead in order
to find the '=' or lack thereof. It may also become difficult for a
human to tell the difference, if lines get too long and complex.
3. "say" is a reserved word, so the instruction says "0". I reject this
option because it has never before been a problem to name a function
"say", and nor should it be a problem.
Now, since I seem to have rejected all of the above interpretations, it
seems that I have no option but to reject the idea of giving a meaning
to functions on the left-hand side of '='.
Ian Collier
Ian.C...@prg.ox.ac.uk | i...@ecs.ox.ac.uk
Wrong. If WHILE were read as a variable, then WHILE=3 would have been 0 and
0 iterations would have occurred. If you have a CMS implementation, try it.
WHILE *is* a variable in this case. WHILE=3 is an assignment
which initializes the variable to 3. The loop is an infinite
loop with the value of While increasing by one at each iteration.
Peter
Interestingly enough, DO (WHILE=3) says invalid expression. Why isn't
this just DO 0?
It's not just my interpretation. It's CMS's.
>Interestingly enough, DO (WHILE=3) says invalid expression. Why isn't
>this just DO 0?
The syntax of the DO command is DO repetitor and one of the
choices for repetitor is name=expri TO exprt BY exprb FOR exprf.
For example, DO i = 1 TO 10 BY 2
I left out the FOR exprf expression. expri is the intial
assignment, in our case, WHILE=3. The book specifically says that
if exprt is omitted (TO exprt), the loop runs
indefinitely unless some other condition terminates it.
---
Later --- I think you have a point. In addition to DO repetitor,
DO can also be followed by an expression which must result in
a number >= 0. DO 0 certainly works so I guess DO (while=3)
should also be interpreted as DO 0. I guess it's a bug.
Peter
But in the end I was not won over by REXX itself, for many of the
reasons that results in critical critiques of Rexx.
So I created created a scripting/batch/programming language that I
call Cmm ('C' minus minus) which is based on the C language. CEnvi
is a Cmm interpreter that my company, Nombas, sells as shareware and
gives many of the benefits of Rexx but with a language that was easier
for me to use and is also easier for others. I'm not saying that
everyone would rather use Cmm than Rexx; whatever floats your boat
is OK by me.
If you'd like to try this $38 alternative to Rexx, then you can receive it
via anonymous ftp from world.std.com in the pub directory. Each version
comes with lotta documentation and sample Cmm files.
CEnvi for OS/2: cenvi21.zip
CEnvi for DOS: cenvid1.zip
CEnvi for Windows: cenviw1.zip
Or mail me and we'll work out a way for you to try this shareware.
According the VM/XA SP System Product Interpreter manual, on page 28:
"The sub-keywords TO, BY, FOR, WHILE and UNTIL are reserved within a
DO instruction, in that they cannot name variables in the expression(s) but
they may be used as the name of the control variable."
In the case of "DO WHILE=3", WHILE is used as the name of the control variable,
so there is no problem. In "DO (WHILE=3)", WHILE is not the name of the
control variable, so it is reserved as a sub-keywword. When considered a
sub-keyword in this context, it results in a syntax error.
Paul S. Wolberg Internet: 2138...@ibm.cl.msu.edu
1322 Oakridge Ave., Apt. 105 BITNET: 21387psw@MSU
E. Lansing, MI 48823 Voice: H: 517-332-2173
W: 517-337-4949
Yup, you're exactly right. Do (WHILE=3) gives an
error, but Do (WHILEX=3) does not.
Peter
>In "say(6)=7", there are various alternative interpretations (in a situation
>where pseudovariables have been implemented):
>
> 1. "say" is not a reserved word because it is not followed by a space.
> As I said, I think this borders on the pedantic, and I reject this
> option.
> 2. "say" is not a reserved word because, as we can clearly see, it is the
> name of a function which is on the left-hand side of '='. I reject
> this option because the lexical analyser which decides whether or not
> "say" is a keyword has to do an enormous amount of lookahead in order
> to find the '=' or lack thereof. It may also become difficult for a
> human to tell the difference, if lines get too long and complex.
Not only that, but it should be perfectly OK to have
say "Did you know that the value of 1e2 = 1 is..."
say 1e2 = 1
say "How about that?"
> 3. "say" is a reserved word, so the instruction says "0". I reject this
> option because it has never before been a problem to name a function
> "say", and nor should it be a problem.
>Now, since I seem to have rejected all of the above interpretations, it
>seems that I have no option but to reject the idea of giving a meaning
>to functions on the left-hand side of '='.
But by rejecting all those options, you have yet another
unacceptable conclusion: say(6) = 7 is illegal. Saying that
implies that either:
say (6) = 7
is illegal - and I reject that because making it illegal requires that
noxious equal-sign lookahead you rejected in (2) - or
say (6) = 7
is legal, which means that it's different from the illegal say(6) = 7,
which means we're being pedantic about spaces, which you rejected.
On the other hand, there are circumstances in which the space between
an identifier and a left parenthesis is significant in REXX, and ANSI
accepts this. So a bit of pedantry is unavoidable.
This forces me to conclude that the only reasonable interpretation is:
say(6) = 7 is a (not-in-the-language-yet) pseudofunction invocation
say (6) = 7 is a call to SAY passing it a boolean expression result
Now, what about if(x==3) and all that?
Left as an exercise for the reader...
x = 'ZZ'
a = '10af'x
SAY a
You might think you'd get the string '10afZZ', but you don't!
And just another plug for SELECT-WHEN-OTHERWISE constructs as being
vastly superior to cascading IF THEN...IF THEN...IF THEN constructs. Not
only are they MUCH easier to read, but they are also, as has been noted,
much safer in limiting the scope of execution.
In CMS Rexx, we have two forms of global "Fortran Common" values - as of
CMS9, we can use the VALUE function to access CMS GLOBALV data (I think
UniREXX does this too), and we can also access any of our parent
generations' Rexx variable pools throguh the EXECCOMM interface. CMS
Pipelines (a truly cool thing) gives the Rexx programmer an easy way to
access the parent's variable pool via this EXECCOMM interface.
>Wait, you guys have lost a major issue. There is in general in the
>language already an enormous difference between "a(3)" and "a (3)".
I don't think I lost sight of that issue...
>All I am suggesting is that it should not matter whether a is on the
>RHS or LHS.
For the purpose of the space or lack thereof, I probably agree.
>Nothing I said required any change in the meaning of
>SAY(6)=7. That is a say instruction now and would remain one.
Now you've just contradicted yourself. If the space is significant,
then SAY(6)=7 is quite different from SAY (6)=7. The latter is clearly
a SAY instruction, but the former, by your logic, should be a
pseudofunction invocation.
>I am
>suggesting changing the interpretation of SUBSTR(A,3,3)='ABC' which
>now is a logical, converted to 0 or 1 and passed to the environment
>and which I suggest should become a function invocation.
Yes, but if this is to be a general syntactical phenomenon, then you
can't treat SAY (or IF, or WHILE) differently.
<deleted stuff>
>Dave Gomberg, role model for those who don't ask much in their fantasy lives.
>GOMBERG@UCSFVM Internet node UCSFVM.UCSF.EDU fax-> (415)731-7797
Are you the same Dave Gomberg who has been mentioned on the radio show
"West Coast Weekend", and did you recently have a letter published in
"Verbatim: The Language Quarterly"?
Perhaps (re your signature) you're busy enough not to need a fantasy life.
--
------------------------------------------------------------------------
Jack Hamilton j...@netcom.com kd6ttl@n0ary.#nocal.ca.us.na packet
Post Office Box Box 281107 San Francisco, California 94128 USA
I am saying that something with the form of an assignment, but which is
not an assignment because the LHS is of the form symbol(var_list) should
be considered a function invocation. SAY(6)=7 is not of the form of an
assignment, it is of the form of a say instruction. Hence it does not
satisfy the hypothesis of the proposal.
>
>>I am
>>suggesting changing the interpretation of SUBSTR(A,3,3)='ABC' which
>>now is a logical, converted to 0 or 1 and passed to the environment
>>and which I suggest should become a function invocation.
>
>Yes, but if this is to be a general syntactical phenomenon, then you
>can't treat SAY (or IF, or WHILE) differently.
IF(3)=4 THEN SAY HI is not of the form of an assignment, it is
of the form of an IF instruction, and not any kind of assignment. Therefore
the hypothesis of the proposal above is not satified and the proposal does
not apply. One could see this as introducing
an ambiguity and resolving the ambiguity in favor of compatibility.
Is that clearer?
>I am saying that something with the form of an assignment, but which is
>not an assignment because the LHS is of the form symbol(var_list) should
>be considered a function invocation. SAY(6)=7 is not of the form of an
>assignment, it is of the form of a say instruction. Hence it does not
>satisfy the hypothesis of the proposal.
Oh, then you mean that
SAY(X) = 7
is in the form of an assignment and therefore is a pseudofunction, but
SAY(6) = 7
is not, and therefore is a SAY instruction. Which leads one to conclude
SAY(X) = 7 is different from SAY (X) = 7
but
SAY(6) = 7 is the same as SAY (6) = 7
Now, try explaining that to the kind of audience that REXX is supposed
to be reaching.
Or perhaps do you mean that SAY is not a symbol and therefore does not
qualify? Then do you propose to make SAY = 7 or X = SAY + 1 illegal?
>IF(3)=4 THEN SAY HI is not of the form of an assignment, it is
>of the form of an IF instruction, and not any kind of assignment.
And what about IF(X) = Y THEN SAY LO? After all, how often do people
write code asking whether 3 is equal to 4? Is that a pseudofunction
because it is in the form symbol(var_list)?
(By the way, both of these would be legal assignments in today's REXX
if you replace IF(...) with FOO.)
And while we're on the topic of var_list, are you saying that
SAY(X,Y,Z) = 7
qualifies but
SAY(X,Y,"foo")
does not? And then, you could not say
SUBSTR(X,1,3) = "bar"
could you, by your definition? Also, you clearly do want to say
things like
PHONY_ARRAY(1) = "value of first element of phony array"
but you are preventing yourself from doing so, unless you make SAY, IF
et al. a special case (none dare say "reserved words").
- seb
Yes to all the above. I am also the Dave Gomberg with the California Judicial
Council legal forms product. But I am not the Dave Gomberg kite flyer
extrordinaire, but I do have 18000 ft of kite line, for setting a world
altitude record. Now if I can only find a kite...
>More interesting would be what
>say value("7>6")
>returns. On TSO/REXX it generates an "Incorrect call to routine"
>error because 7>6 is not a valid variable name.
>(On the other hand, value(7>6) also ought to return an error,
>because 1 is not a valid symbol name.
>In Regina, value(7>6) returns 1, and value("7>6") returns 7>6.
>At least it's consistent, which is superior to what TSO/REXX does.
TSO/REXX is correct (I would say that, because REXX/imc does the same thing
:-) ).
"1" is a symbol (a constant one, but a symbol nevertheless). Given that
datatype("1","S") returns 1 and symbol("1") does not return "BAD", I would
expect value("1") not to give an error.
imc
>Not only that, but it should be perfectly OK to have
> say "Did you know that the value of 1e2 = 1 is..."
> say 1e2 = 1
> say "How about that?"
No problem with the above because say is obviously not a function call. It
isn't followed by a parenthesis.
>But by rejecting all those options, you have yet another
>unacceptable conclusion: say(6) = 7 is illegal.
I didn't imply that at all. I said that the only way to make that legal and
to give it a well-defined meaning was to reject the "pseudovariable" idea.
If I reject that, then the above is certainly legal and has a well-defined
effect. I didn't mean that all things which look like pseudovariables
should be classed as illegal!
>On the other hand, there are circumstances in which the space between
>an identifier and a left parenthesis is significant in REXX, and ANSI
>accepts this. So a bit of pedantry is unavoidable.
Possibly, and if you require a space between a keyword and a
left-parenthesis then you are only re-using a rule which is already in
existence. But then, I can always say "breakage!" :-)
imc
Rexx already has reserved words. The crypt has already been violated by X
and B, which really really really are reserved words already. I do not
like them, and I do not think there should be any more of them, but they
would not be a violation of some sacred purity.
THE BOOK (Cowlishaw) says about functions: "It is important to note that
the name of a function, FUNCTIONNAME, must be adjacent to the "(", with
*NO* blank in between, or the construct will not be recognized as a
function call. (A BLANK OPERATOR will be assumed at that point
instead.)"
I think this is pretty clear. If left-functions were to exist, then:
SAY(X) = 46 <-- call to the SAY function
SAY (X) = 46 <-- SAY command, will type something
There is a working example of left-functions in the SPSS language.
(Mainframe, Unix, Windows, but not PC, versions). It presents no
particular parsing problems, and some functions such as SUBSTR are
extremely useful on the left. I use them frequently when building SPSS
applications. They are totally intuitive to use. I want them in REXX.
And now for some more productive implementation details about
left-functions: How to support left-functions written in the REXX
language. I propose that it all be done through the variable RESULT.
With a left-function the result is the input, not the output. Upon entry,
a function which might be called as a left-function should do:
IF (SYMBOL('RESULT') = 'VAR') THEN DO
/* The code for being called as a left-function. This code uses the
value of variable RESULT as input. If the function was not intended
to be called as a left-function, produce error messages here. */
END
ELSE DO
/* The code for being called as an ordinary right-function. This
code sets RESULT via the RETURN statement. */
END
All the usual caveats about variable RESULT apply - but even more so.
The system can change its value on you, so if you value its value, save
it in some other variable before doing much of anything.
There are doubtless some ramifications of this which would need to be
worked out, but I present this as one way of implementing this which
would be consistent with what we already have.
Roger Deschner, University of Illinois at Chicago U52...@UICVM.UIC.EDU
>And now for some more productive implementation details about
>left-functions: How to support left-functions written in the REXX
>language. I propose that it all be done through the variable RESULT.
>With a left-function the result is the input, not the output. Upon entry,
>a function which might be called as a left-function should do:
>
> IF (SYMBOL('RESULT') = 'VAR') THEN DO
> /* The code for being called as a left-function. This code uses the
> value of variable RESULT as input. If the function was not intended
> to be called as a left-function, produce error messages here. */
> END
> ELSE DO
> /* The code for being called as an ordinary right-function. This
> code sets RESULT via the RETURN statement. */
> END
This presumes that the variable RESULT is unset on entry to a function.
Unless the function is a PROCEDURE, this isn't currently true, and is
therefore another change required to the language in order to make
this work - and an incompatible one at that.
I think a better model is to treat
LEFTFUNC(A,B,C) = D
as a call to
LEFTFUNC(A,B,C,D)
If I'm not mistaken, that's what Dave Gomberg describes in his
implementation. (Hey, I said "IF"...)
Also, this follows an established model: the Lisp array handling
macros in MacLisp (am I showing my age here?). The drawback is
that you are constrained to use functions that take a fixed number
of arguments.
Perhaps the best approach would be to wait for one of the Object
Oriented REXX implementations to come out with operator overloading
a la C++. Then we could overload the assignment operator, so that:
FOO~BAR = some(expression)
would behave meaningfully.
> I'm curious as to the reason for not "short-circuiting" the
> evaluation.
When an expression is short-circuited, the side-effects of the skipped
part of the expression are 'lost'. Sometimes you want that, and
sometimes you don't ...
The two most common situations where I've seen short-circuits in use
are:
1) In C, in order to protect the evaluation of an expression involving
pointers against a segmentation fault, something like:
if (ptr && ptr->next && ptr->next->value)
...
2) In Perl, as a sort of flow-control 'statement':
open FILE || die "Could not find file\n" ;
The first case is fairly irrelevant for a programming language like
Rexx, which doesn't have pointers. The second case is just an 'obscure'
(but very terse) form of an IF-statement, which will not be of much use
in Rexx (Rexx functions don't even return suitable values for such
short-circuit use).
Thus, the main advantage of short-circuits in Rexx is to optimize code
wrt speed. Although speed is a nice feature, it is hardly more
important than readability.
Another advantage of a proposed Rexx-specific use of short-circuit
expression evaluation is that you can write things like:
if symbol(foo)='VAR' & foo='bar' then
...
Where short-circuiting are used as a guard against NOVALUE triggering,
similar to the C example above. IMO, the best reason for not having
short-circuit evaluation is that simulating non-short-circuit in a
short-circuit language is worse than the opposite:
Simulating short-circuit in Rexx can make a few extra levels of
nesting, but it is very obvious what the meaning of the code is:
if a then
if b then
...
Simulating non-short-circuit in a short-circuit language often
uses temporary storage of one kind or another, e.g. temporary
variables, or forces evaluation by making local operations a
normal function
tmp_a = a
tmp_b = b
if a & b then
...
or even more elaborate methods (using C syntax):
if ( a ? b : ( b && 0 ))
...
Regards,
-anders <and...@pvv.unit.no>
>The two most common situations where I've seen short-circuits in use
>are:
>
> 1) In C, in order to protect the evaluation of an expression involving
> pointers against a segmentation fault, something like:
>
> if (ptr && ptr->next && ptr->next->value)
> ...
>
> 2) In Perl, as a sort of flow-control 'statement':
>
> open FILE || die "Could not find file\n" ;
>
>The first case is fairly irrelevant for a programming language like
>Rexx, which doesn't have pointers. The second case is just an 'obscure'
>(but very terse) form of an IF-statement, which will not be of much use
>in Rexx (Rexx functions don't even return suitable values for such
>short-circuit use).
This strikes me as a classic strawperson argument: giving only two
examples, one with pointers and one with a Perl construct that REXXers
will be guaranteed to find arcane.
Saying that REXX doesn't have pointers doesn't deny the need for
short-circuiting a la (1). What if we had given this example:
(in C) if (x != 0 && y/x > foo) ...
Obviously the same need applies to REXX, and any programming language.
Same goes for
if (x == 1 || very_expensive_procedure(x) == 2) ...
These are not rare occurrences.
>Thus, the main advantage of short-circuits in Rexx is to optimize code
>wrt speed. Although speed is a nice feature, it is hardly more
>important than readability.
The non-short-circuit implementation that compels the nested-IF usage
sacrifices both speed and readability. (If REXX can have a partially
functional SELECT because people believe that it's both faster and more
readable than a series of IF's, then wouldn't the same people believe
that a single logical expression is also both faster and more readable
than the nested IF's?)
> Simulating short-circuit in Rexx can make a few extra levels of
> nesting, but it is very obvious what the meaning of the code is:
>
> if a then
> if b then
> ...
>
> Simulating non-short-circuit in a short-circuit language often
> uses temporary storage of one kind or another, e.g. temporary
> variables, or forces evaluation by making local operations a
> normal function
>
> tmp_a = a
> tmp_b = b
> if a & b then
> ...
Yes, but when does anyone specifically need non-short-circuiting? I
can't think of a case offhand, and if there is such a case, then it
clearly should be coded explicitly so that the reader can see the
sequential nature of the operations.