Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

RPL: Reassign a local variable

238 views
Skip to first unread message

oliverue

unread,
Jan 8, 2011, 6:16:27 AM1/8/11
to
Would someone please confirm to me that I'm not (again) missing
something added in the 49-line, and that there's indeed no way to
reassign a local variable in RPL?
That is, given
<< -> x << blabla >> >>
there's no way to write something in blabla that will set x to a new
value.

I'm thinking of the "-> ... <<" combo as establishing a new local
context, so this is unsurprising.

However, it seems to me that this would be the single best thing one
could do to enhance the language, and I'm more than tempted to put
this into the RPL implementation inside MorphEngine, by purposing the
"=" operator.
That is,
<< -> x << @ assign x to val on stack
3 x = @ reassign x to 3
x @ put 3 on stack
>> >>

Yes, you could "simply" use globals variables (STO and a name), but I
this is slow and I fear the practice of doing so is routinely killing
performance of UserRPL code. (Plus, you need annoying cleanup code.)

I would very much appreciate your feedback on whether or not you think
this would be a good feature.
If you like it, would you prefer this
3 'x' =
syntax?

Thanks!

Alain Montfranc

unread,
Jan 8, 2011, 6:21:24 AM1/8/11
to
oliverue a utilisé son clavier pour écrire :

> Would someone please confirm to me that I'm not (again) missing
> something added in the 49-line, and that there's indeed no way to
> reassign a local variable in RPL?
> That is, given
> << -> x << blabla >> >>
> there's no way to write something in blabla that will set x to a new
> value.
>

On my 48 << 3 -> x << 5 'X' STO X >> >> returns 5 showing that the
local variable X changed ?


oliverue

unread,
Jan 8, 2011, 8:18:18 AM1/8/11
to

> On my 48 << 3 -> x << 5 'X' STO X >> >> returns 5 showing that the
> local variable X changed ?

Hi Alain. Actually: you just created a new global variable and
recalled it. The local variable, x, is unchanged.

Global variables are *much* slower than local ones, especially writing
to them. If you write to global vars in an inner loop, you will
invariably see poor performance on compute-intensive code. The only
way to work around this is to use the stack. That's often quite
inconvenient and difficult to do.

oliverue

unread,
Jan 8, 2011, 8:22:14 AM1/8/11
to
Use the stack, or create another local var block, that is. Latter is
also expensive, bloats the code, and isn't always a solution.

Raymond Del Tondo

unread,
Jan 8, 2011, 8:23:24 AM1/8/11
to
Of course you can reassign a value of a local variable,
at least as long the context is reachable.

Alain meant to write:
<< 3 -> x << 5 'x' STO x >> >> (small x chars)
which returns 5, of course.

HTH


"oliverue" <oliv...@hotmail.com> schrieb im Newsbeitrag
news:7e74fd4f-ae37-4d5c...@fx12g2000vbb.googlegroups.com...

Andreas Möller

unread,
Jan 8, 2011, 8:35:02 AM1/8/11
to
Hello,

and that's what came through mind while reading this ;-)

Note that you can access any 'named' LAM as a NULLLAM, too.

<< 3 -> X
<< 'X' RCL 5 'X' STO 'X' RCL
>> EVAL
>>

or for copy and paste:
::
x<<
Z3_
xRPN->
LAM X
x<<
x'
LAM X
xENDTIC
xRCL
Z5_
x'
LAM X
xENDTIC
xSTO
x'
LAM X
xENDTIC
xRCL
x>>ABND
xEVAL
x>>
;
@

Regards,
Andreas
http://www.software49g.gmxhome.de

oliverue

unread,
Jan 8, 2011, 9:28:09 AM1/8/11
to
Wow. So simple, yet is the answer to my question. I verified scope and
non-evaluated recall make this behave like the local variable store I
sought (and overlooked). Thank you very much!

I knew that STO is desperately slow, so I didn't bother checking if
this works as expected with a local name. A little test
<< -> X << 1 10000 START 5 'X' STO NEXT X >> >>

reveals it's 2-3 faster than doing a global store, but it's still
very, very slow: Approx. 1000 stores/s on Emu48/49G. That translates
into a tiny number on an actual device.

I'm left wondering if this does go a global store that's simply not
"committed" because the internal cleanup code for the declared local
kicks in upon context block termination.
Anyway, the slow speed then becomes an implementation detail. The
language itself does seem to have a provision, even if it's by some
side effect. (The 49G Advanced Reference for STO is silent on this
matter.)

Cheers.

Alain Montfranc

unread,
Jan 8, 2011, 10:37:18 AM1/8/11
to
oliverue avait soumis l'idée :

>> On my 48 << 3 -> x << 5 'X' STO X >> >> returns 5 showing that the
>> local variable X changed ?
>
> Hi Alain. Actually: you just created a new global variable and
> recalled it. The local variable, x, is unchanged.
>

No, there's no global variable remaining at the end of the program
execution

Alain Montfranc

unread,
Jan 8, 2011, 10:38:17 AM1/8/11
to
Raymond Del Tondo a émis l'idée suivante :

> Of course you can reassign a value of a local variable,
> at least as long the context is reachable.
>
> Alain meant to write:
> << 3 -> x << 5 'x' STO x >> >> (small x chars)
> which returns 5, of course.
>

yeap, sorry, to much stupid case insensitive windows these days :-(


Andreas Möller

unread,
Jan 8, 2011, 11:10:25 AM1/8/11
to
Hello,

the “slowness” comes from the overhead of type checking that is done
here by the USER-RPL commands at each loop run and that you are
modifying the loop counter which requires an addition. Also here in
USER-RPL the O.S. needs to keep track of several temporary
environments which is not very effective, specific here are 2 areas
for the LAMs.

Storing into a named LAM depends on how fast the LAM is found while
searching through the temporary environments of the LAMs. The storing
itself is very fast as only a 5 nibble pointer is stored.

In your provided example xSTO needs around
Elapsed: 1,9715ms
for each store.
The actually storing after the type checking is
Elapsed: 0,3605ms

As a side note: Storing into a NULLLAM needs
Elapsed: 0,04575ms

All timings where done in debug4x, the Saturnator machines are
actually faster.

As you see from the above the “slowness” comes clearly from the
overhead of each USER-RPL command which is there on purpose to prevent
crashing the machines by using the “wrong” commands but, as always,
there is a price to pay for this “user-safe” programming environment.

Basically it boils down to the fact that the machine is slow if the
user does not know what he/she is doing ;-)

Hope this helps,
Andreas
http://www.software49g.gmxhome.de

oliverue

unread,
Jan 8, 2011, 2:49:14 PM1/8/11
to
Hi Andreas,

Thanks for your responses.

I'm a user who doesn't know what he's doing, but I'd still like my
stores to be fast. ;-)

>that you can access any 'named' LAM as a NULLLAM, too.

I'm ignorant about LAMs, let alone NULLLAMs, and didn't figure out how
to read your RCL, STO, RCL example. Would you mind to elaborate on
that?

Is there a way to get NULLLAM store performance from UserRPL, or is
this all strictly SysRPL? (I would glean that from what you said, but
your example was in UserRPL.)

Andreas Möller

unread,
Jan 8, 2011, 3:07:11 PM1/8/11
to
Hello,

> I'm ignorant about LAMs, let alone NULLLAMs, and didn't figure out how
> to read your RCL, STO, RCL example.

All of this can be found in this newsgroup and/or the documentation
published by HP.

> Is there a way to get NULLLAM store performance from UserRPL,

Nope (except through SYSEVAL - evaluates unnamed operating system
objects specified by their memory addresses).
USER-RPL is a subset of System-RPL.
Eventually all USER-RPL commands call System-RPL which itself ends up
in Saturn-ML (which - in case of the ARM based machine - might call
some optimized ARM routines, e.g. moving memory is done that way).

And, of course, the speed of your code depends on how close you get to
the hardware and the efficiency of your code ;-)

Nemo

unread,
Jan 8, 2011, 6:44:14 PM1/8/11
to
On Jan 8, 3:28 pm, oliverue <olive...@hotmail.com> wrote: (..)

Hi Oliverue,

You can do the same thing with locals and globals variables. I think
that the only difference is that locals variables mlust be evaluated
(EVAL) if they contains a program object (i don't know why)

About speed :


« 0 -> x « 1 1000 START 5 'x' STO NEXT » »
'v1' STO

« 0 -> x « 1. 1000. START 5 'x' STO NEXT » »
'v2' STO

« 0 -> x « #1d #1000d START 5 'x' STO NEXT » »
'v3' STO

« #1d #1000d START 5 'X' STO NEXT »
'v4' STO


« #1d #1000d START 5 NEXT » @ put on the stack only
'v5' STO

« 0 -> <-x « #1d #1000d START 5 'Žx' STO NEXT » »
'v6' STO

« DUP TICKS
-> p t

« EVAL
p " -> " + TICKS t - B->R 8192 / 3 RND + " s" +
»
»
'Bench' STO

@ 'v1' Bench 2.885 s
@ 'v2' Bench 2.877 s
@ 'v3' Bench 2.663 s
@ 'v4' Bench 10.504 s
@ 'v5' Bench 0,749 s
@ 'v6' Bench 2.647 s

The fastest way is to use the stack, the worst a global variable. But
i agree with you that even the fastest way is too slow ;)

Gilles

oliverue

unread,
Jan 9, 2011, 8:17:10 AM1/9/11
to
@Andreas: ok, thank you. That's helpful information.

@Gilles: thanks for sharing those test results.

John H Meyers

unread,
Jan 10, 2011, 4:24:05 PM1/10/11
to
On 1/08/2011 7:23 AM, Raymond Del Tondo wrote:

> Alain meant to write:
> << 3 -> x << 5 'x' STO x >> >> (small x chars)
> which returns 5, of course.

As to "slowness" when storing into global variables,
whenever you either create, purge, or store something
having a _different length_ into a global variable,
all storage between that point and the end of HOME
must be moved in memory, to accommodate that change,
which accounts for quite a bit of overhead in those cases.

However, no such move (nor any temporary memory)
is required when storing an item of the same length
into a pre-existing global variable,
and the execution time of such constant-length storing
should therefore be somewhat less than in the other cases.

This is one of the reasons why internal ROM functions
often use the "hidden directory"
to store items temporarily as global variables,
since the amount of memory needing to be moved
is least when using that directory for storage,
and no additional "temporary environments"
or temporary memory is used at all in that case.

Another factor influencing overall timing
is the time spent during periodic "Garbage Collection,"
which involves everything stored in volatile areas,
but affects nothing stored in global variables.

Items stored in global variables do not participate in GC;
this is the very reason why, in the HP48G[X][+],
the SORT command temporarily stored the list being sorted
into the hidden directory, which greatly sped up sorting
in many cases, particularly for large lists.

A blanket statement such as "everything will be faster
using local variables" is not always the case;
neither is it always a simple matter to evaluate
the "bottom line" effect of certain decisions,
given various different factors at play.

The same thing applies to the rest of the world as well,
despite politicians always having their own single
best answer to everything.

-[ ]-

Bruce Horrocks

unread,
Jan 11, 2011, 6:06:10 PM1/11/11
to
On 08/01/2011 13:18, oliverue wrote:
>
>> On my 48<< 3 -> x<< 5 'X' STO X>> >> returns 5 showing that the
>> local variable X changed ?
>
> Hi Alain. Actually: you just created a new global variable and
> recalled it. The local variable, x, is unchanged.

But only because he got the case wrong. Try:


<< 3 -> x << 5 'x' STO x >> >>

--
Bruce Horrocks
Surrey
England
(bruce at scorecrow dot com)

oliverue

unread,
Jan 11, 2011, 8:17:30 PM1/11/11
to
>As to "slowness" when storing into global variables

John,

Thanks for sharing your insights. Very interesting.

I hope I will be forgiven to continue to write slowness without
quotes.
This
\<< 1 10000 START NEXT \\>
taking 2.5s in Emu48 on a 600 MHz ARM machine (estimated ~10s on 50g)
*is* slow. (No type checking here...)
Slower than a C64 (1 MHz) running BASIC in 1984.

And that's ok. It is what it is. The implementation was probably never
intended to be a speed daemon, but to provide a powerful, type-rich
environment. Which--arguably unsurpassed, if you look at the whole
picture--it remains to offer. (As far as calculators and mobile math
systems.)


Thanks to the info given in this thread, I implemented correct local
var writing in my interpreter. Thank you.

oliverue

unread,
Jan 11, 2011, 8:21:29 PM1/11/11
to
On Jan 8, 4:38 pm, Alain Montfranc <m...@aux.spammeurs> wrote:

> > Alain meant to write:
> > << 3 -> x << 5 'x' STO x >> >> (small x chars)
> > which returns 5, of course.
>
> yeap, sorry, to much stupid case insensitive windows these days :-(

Alain,

I'm sorry for not capturing your intention right away. Guess my mind
was set on seeing STO as the command to store globals only. Thanks for
setting me straight!

Oliver

John H Meyers

unread,
Jan 13, 2011, 1:11:54 AM1/13/11
to
On 1/11/2011 7:17 PM, oliverue wrote:

> \<< 1 10000 START NEXT \>>
> taking 2.5s in Emu48 on a 600 MHz ARM machine

There is no good reason, other than trying to
deliberately sabotage results, for ever using
_integer_ objects where _real_ values might be far more efficient.

However, 2.5s may actually be normal for reals --
it takes about 0.25_s on my Emu48 (of course,
mine is on Windows with a particular processor,
different from the above, so such comparison is useless).

It so happens that START has a "dispatch" for two integer arguments,
in which the difference between arguments is first computed,
then converted to real, then used as a second argument
(with real 0. as the first argument), which is only
a very slight waste of time over using two real arguments,
but any waste at all is a consequence either of ignorance,
or of not using intelligence, or of deliberately hobbling
a procedure which could be done more efficiently,
so what does that illustrate?

I have never found anything for which I would ordinarily
use a calculator, which is other than practically instantaneous
on my own Emu48, but of course, were I to attempt
to re-design the original atomic bombs,
using only my real calculator for computations,
that might be an example of where a faster calculator
would have paid off.

However, the use of computers can pay off much more,
so why would nuclear engineers turn to handheld calculators,
which were aimed at those wanting a mathematical _scratchpad_
(and currently aimed at whom?)

If you want to speed up something which is potentially very useful,
but has been poorly (yet safely) programmed in UserRPL
and can be slow with long lists, how about the INLIST program,
which more or less extends INFORM to accommodate unlimited input,
in a manner that I'm surprised was not thought of to begin with,
during HP48 G-series design:

http://groups.google.com/group/comp.sys.hp48/msg/c18173a5066b36f9

Yes, that's an interactive program, but interaction,
rather than cracking RSA,
is basically what these calculators were created for.

That program might also be far more intelligently designed,
and if also written in SysRPL might become more efficient,
but substantial use wasn't much expected, so it was done
to minimize programming time instead.

A basis for doing really long computations in a calculator's style
can't be bad, and seems attractive to create,
but if it starts getting too complex
(e.g. having to explicitly "type" every variable),
some of that original "scratchpad" charm might scratch right off,
just like some of the original HP key printing (and the keys themselves :)

[r->] [OFF]

oliverue

unread,
Jan 13, 2011, 5:21:36 AM1/13/11
to
John,

I understand your point about integer vs. float. Note, though, that
Gilles showed in his benches #1 and #2 that integer vs real didn't
matter for loop setup vars, which is expected as "setup" is "one-
time". I had confirmed, and confirm again, that the run-time is the
same.
I wrote "1 10000 START" because I'm not in the habit of writing non-
floats with dot and because I had previously seen it not to matter.
(No sabotage here...)
(MorphEngine does not support the concept of writing a non-float real
with a dot, btw. And I don't think I will adopt it (but rather, on
import, change those values into their standard notation
counterparts), as this seems to be a feature that will trip users
constantly.)

Your points about a calculator not needing to be faster to be useful
are taken. I've no idea if what I'm building will ever be embraced for
this aspect, but I'm hoping so. It'll be one entry into the great
selection machine of evolution in software.

Thanks for pointing me to your INLIST program. I'm going to take a
good look at it. Interactive commands need to be implemented
differently in my engine, because just emulating them would deny what
HTML5 and JavaScript bring to the table. Anyway, just run on the
faster substrate, your program might be fast enough. (The default
speed on the mobile device is roughly Emu48 speed on computer for
something like this, I suspect.)

Suggested typing being *optional*, I don't see how it would hurt.
Whoever wants to see their UserRPL programs run faster, types their
local variables. Sounds easy enough to me. (And less prone to accident
than having to be careful about typing "1." instead of "1")

Cheers.

Joel Koltner

unread,
Jan 13, 2011, 8:28:46 PM1/13/11
to
"oliverue" <oliv...@hotmail.com> wrote in message
news:e69f9937-85f9-4f5a...@w29g2000vba.googlegroups.com...

> Slower than a C64 (1 MHz) running BASIC in 1984.

Hey, there's no shortage of very-high-level languages that make multi-GHz PCs
run about as fast as a C-64 -- try Python some time! :-)

John H Meyers

unread,
Jan 13, 2011, 11:48:50 PM1/13/11
to
On 1/13/2011 4:21 AM, oliverue wrote:

> I understand your point about integer vs. float. Note, though, that
> Gilles showed in his benches #1 and #2 that integer vs real didn't
> matter for loop setup vars, which is expected as "setup" is "one-
> time".

If it weren't that in the particular case [START...NEXT]
the original integer arguments are converted to reals
(or more precisely, to a real zero
and then the real difference between the original integers)
then it would matter a great deal, if the subsequent counting
and comparisons were all done via integer arithmetic,
rather than real arithmetic.

Consider this, for comparison,
in which we simply change "START" to "FOR n":

\<< 1 10000 FOR n NEXT \>>

Now it makes a very significant difference,
as to whether our "setup arguments" are reals (faster)
vs. integers (slower), because all the arithmetic
is now _integer_ arithmetic.

Here's another comparison, in which we expect
the same number of iterations as above to be performed:

\<< 10000000000000001 10000000000010000 START NEXT \>>

\<< 10000000000000001 10000000000010000 FOR n NEXT \>>

The first of the above takes about the same time as above,
but the second increases even more in time,
because of the length of each integer arithmetic operation,
performed in memory instead of in Saturn CPU registers
(whether real or emulated).

Other previous examples that you gave also had
some significant built-in waste, such as using "lim"
for a fixed computation, and then also doing that computation
in a very inefficient way, much as would be using a FOR...NEXT loop
to sum the first 10,000 integers, rather than using a direct formula
sometimes attributed to Gauss (although actually known earlier).

If you are writing a system intended to mimic UserRPL,
aren't some of these same considerations of interest to you,
or is it intended that faster processing
will obviate any interest in optimizing algorithmic design?

In all cases, one must still be on the lookout for the fact
that in floating point work, it takes much attention
to be reliably accurate and correct across all possible inputs,
which HP has been well known for giving much attention
and doing very carefully (almost always, anyway :)
often thanks to Prof. William Kahan:

http://en.wikipedia.org/wiki/William_Kahan

[r->] [OFF]

0 new messages