RPL+

Discussions about the RPL and RPL+ programming languages

RPL+

Postby oliver » Wed Jan 19, 2011 4:02 am

Since version 1.3, the RPL implementation in MorphEngine fully implements the HP UserRPL language as present in the hp 50g (and a partial command set).
Versions 1.3.5 and higher, also feature significant language additions.

We call the combination of traditional RPL with those additional features "RPL+" (http://naivedesign.com/ND1/ND1_Reference__RPL+.html).

These features are (or will be, for those marked "next update" or "planned"):

. operator:
Code: Select all
folder.program
permits function/program program to be called in folder folder

Local variables improvements
fused = operator for defining/assigning, and re-defining/re-assigning at any time, a local variable:
Code: Select all
=x
instead of
Code: Select all
→ x ≪ ... ≫
and any subsequent
Code: Select all
'x' STO

fused =: operator working like = but not consuming the top-of-stack item:
Code: Select all
=:x
instead of
Code: Select all
→ x ≪ x ... ≫
or any subsequent
Code: Select all
'x' STO x

fused =+, =*, =-, =/ operators for adding to, multiplying, subtracting, dividing, respectively, a local variable:
Code: Select all
=+x
instead of
Code: Select all
'x' STO+
etc.

fused [] operator for one- and zero-based array read access:
Code: Select all
x[expr]
instead of
Code: Select all
'x' [commands to implement expr] GET    or the equivalent     'x(expr)' EVAL

For example, for expr := j-k
Code: Select all
x[j-k]
instead of
Code: Select all
'x' j k - GET    or the equivalent     'x(j-k)' EVAL

Code: Select all
x[j-k:0]
instead of
Code: Select all
'x' j k - 1 + GET    or the equivalent     'x(j-k+1)' EVAL

Indexes wrap, permitting circular buffers (unless a system flag is set to prevent this behavior).
For (j-k) > length of array x:
Code: Select all
x[j-k]
instead of
Code: Select all
'x' j k - x SIZE LIST→ MOD 1 + GET    or the equivalent     'x(MOD(j-k, [sizeOfX])+1)' EVAL

fused =local[] operator for one- and zero-based array write access:
Code: Select all
=x[expr]
instead of
Code: Select all
'x' [commands to implement expr] PUT    or the equivalent     'x(expr)' STO

For example, for expr := j-k
Code: Select all
=x[j-k]
instead of
Code: Select all
'x' j k - PUT    or the equivalent     'x(j-k)' STO

Code: Select all
=x[j-k:0]
instead of
Code: Select all
'x' j k - 1 + PUT    or the equivalent     'x(j-k+1)' STO

For indices j > length of array x, an error is thrown, except the element following the last can be assigned.
E.g., for x := [1 2 3]
Code: Select all
=x[4]
instead of
Code: Select all
'x' SWAP +

fused =:local[] operator for zero-based array write access, without consuming top-of-stack item:
Code: Select all
=:x[j-k]
instead of
Code: Select all
DUP 'x' j k - PUT    or the equivalent     DUP 'x(j-k)' STO

and
Code: Select all
=:x[j-k:0]
instead of
Code: Select all
DUP 'x' j k - 1 + PUT    or the equivalent     DUP 'x(j-k+1)' STO

and, for j == length of array x:
Code: Select all
=:x[j]
instead of
Code: Select all
DUP 'x' SWAP +

fused [] operator for one- and zero-based string read access:
Code: Select all
x[expr]
instead of
Code: Select all
'x' commands-to-implement-expr  DUP SUB

For example, for expr j-k
Code: Select all
x[j-k]
instead of
Code: Select all
'x' j k - DUP SUB

Code: Select all
x[j-k:0]
instead of
Code: Select all
'x' j k - 1 + DUP SUB

fused ++ and -- operators:
Code: Select all
++x
instead of
Code: Select all
'x' 1. STO+     or      'x' 1 STO+

stand-alone ++ and -- operators:
Code: Select all
++
instead of
Code: Select all
1. +     or      1 +


"Default to zero-based indexing" command:
Code: Select all
0[]
This permits to drop any ":0" in subsequent array accesses. If one-based indexing is desired, specify ":1".

"Permit hash table indexing" command:
Code: Select all
any[]
This permits any object (specifically, any number or string) to be used as an index. This extends arrays to hash tables / maps.

extended scope of local variables: inner-frame (called) RPL code has access to the local variables of outer-frame (calling) RPL code. Inner frame local variable definitions supersede outer frame variables of the same name

BREAK, CONTINUE and IFTB, IFTC for early termination/continuation in loops. x IFTB and x IFTC are shortcuts for IF x THEN BREAK END and IF x THEN CONTINUE END, respectively.

Immediate algebraic expressions: algebraic expressions may appear without '' (single quote) type decorators, for immediate evaluation. They must not contain white space characters.
Code: Select all
expr
instead of
Code: Select all
'expr' EVAL

(planned) optional typing of local variables:
Code: Select all
 → x:real v:vector ≪ ...
or
Code: Select all
=x:real =v:vector
instead of
Code: Select all
→ x v ≪ ...


Array (list, vector) improvements
- parallel processing extended to vectors: list processing commands, and automatic list processing, is available for vectors
- new "language-close" commands: insert, remove, (planned)push, (planned)pop, and more (see http://naivedesign.com/ND1/ND1_Reference__Function_Summary.html)
- nonEmptyResult variable: DOSUBS and DOLIST set a new global variable, which indicates whether or not their operation yielded new items on the stack

There are other RPL-related features in MorphEngine.
For example, RPL can call JavaScript. This is a strong feature by itself, but we're not considering this a language feature, as it has no bearings on the syntax. So this is not considered part of RPL+.
Similarly, there's a growing number of MorphEngine-specific (i.e., non-HP) commands and data types (such as the image data type and associated functions). These augment what you can do in RPL, but, too, are not language features per se, and therefore not listed here.

This post will be updated with new or planned RPL+ features, as they become available.

If there're language-level improvements you'd like to see in RPL, or if you see a reason not to implement a planned feature, please reply with your suggestions. Thank you for your input.
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

Recursion using local variables

Postby oliver » Fri Feb 25, 2011 5:05 am

Here's an example of "extended scope of local variables" in RPL+.

In RPL you cannot easily use local variables to define a recursion. In RPL+ you can.

RPL
Code: Select all
≪ 0 -> Euclid ≪ {DUP {SWAP OVER MOD Euclid EVAL} {DROP} IFTE} 'Euclid' STO Euclid EVAL ≫ ≫


RPL+
Code: Select all
≪ {DUP {SWAP OVER MOD Euclid EVAL} {DROP} IFTE} → Euclid ≪Euclid EVAL≫ ≫

or, using the RPL+ assignment operator,
Code: Select all
≪ {DUP {SWAP OVER MOD Euclid EVAL} {DROP} IFTE} =Euclid Euclid EVAL ≫

See http://www.hpmuseum.org/cgi-sys/cgiwrap/hpmuseum/archv016.cgi?read=100320 for a full discussion on this topic.

Note, in RPL+, you can still write the code like in the RPL example. RPL+ adds to the RPL language. You pick and choose which parts you'd like to use.
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

RPL+ Example: Partitions of an integer

Postby oliver » Sun Apr 03, 2011 6:12 am

Example for use of RPL+ fused operators:

This code
Code: Select all
≪ → n ≪
   0. 'n>180' {R→I} IFT → zero ≪
   0. → k ≪
   n mkpents → pents ≪
   [0. 1. 1. 0.] → signs ≪
   {1.} → partitions ≪
   1. n FOR j
      zero @ running sum
      1. 'k' STO
      WHILE
         j pents k GET -
         DUP @ for re-use next
         0. >=
      REPEAT
         partitions SWAP 1. + GET
         signs k 4. MOD 1. + GET {+} {-} IFTE
         'k' 1. STO+
      END
      DROP
      'partitions' SWAP STO+ @ add to partitions
   NEXT
   partitions n 1. + GET
≫≫≫≫≫≫≫


becomes
Code: Select all
≪ =n
   0 'n>295' {R→I} IFT =zero
   n mkpents =pents 
   [0 1 1 0] =signs
   {1} =partitions 
   0[] @ default to zero-based indexing
   1 n FOR j
      zero @ running sum
      1 =k
      WHILE
         j pents[k] - =:diff
         0 >= 
      REPEAT
         partitions[diff]
         signs[MOD(k,4)] {+} {-} IFTE
         ++k
      END
      =partitions[j] @ add to partitions
   NEXT
   partitions[n]



Defaulting to zero-based indexing (via inserted enable command), it's easy to see how this RPL code
Code: Select all
         partitions SWAP 1. + GET

becomes
Code: Select all
         partitions[diff]


The [] operator's expression ability is used, when changing this
Code: Select all
         signs k 4 MOD 1 + GET {+} {-} IFTE

into
Code: Select all
         signs[MOD(k,4)] {+} {-} IFTE

Seeing that MOD(k,4) implements a circular buffer on signs, this may be further simplified to
Code: Select all
         signs[k] {+} {-} IFTE

which exploits the automatic circular buffer feature of the [] operator.

Instead of holding the intermediate result produced in the condition block on the stack, it is more efficiently cached in the diff local variable (which, thanks, to the no-consume attribute ":" added to the = operator, does not affect the stack), for re-use in the REPEAT block. This leads to two case-dependent (and not easily grasped) DUP and DROP statements to disappear.

One local initialization disappears, because the fused "=" operator allows to simply assign the local at any time. (That is, there's no distinction between defining a local and then re-assigning it, as in RPL.)

Overall, the RPL+ code is more concise and easier to read.

Write-ability and readability are the main motivations with a code change like this.
Another upshot is this: the code runs faster. (In this case, 300% and 700%, for Real and BigInt values in partitions, respectively.)
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

RPL+ Example: String-to-list

Postby oliver » Thu Apr 21, 2011 9:24 am

Here's an example showing the use of the [] operator, used to look up characters of a string.

The task at hand is to convert a string into an array of its char codes.

Based on discussion at http://groups.google.com/group/comp.sys.hp48/browse_thread/thread/7f75389466b35e52/29afde07e7eb2ff9, the fastest User RPL for this task is
Code: Select all
«
 DUP SIZE DUP
 -> s
 «
  1 SWAP START
    DUP NUM SWAP TAIL
  NEXT
  DROP s ->LIST
 »
»

This is not trivially found code, and was determined to be the fastest after discussion participant Gilles compared 8 different implementations.
It's runs in 0.29s on ND1 for a 500-char string.

Here's the RPL+ version:
Code: Select all
« =s
  {} =list
  1 s SIZE FOR i
    s[i] NUM =list[i]
  NEXT
  list
»

This code *is* trivially found.
It runs in 0.13s.

(For more details and additional implementation options on ND1 and on HPs, see the discussion link above.)

EDIT:
With implied MAPing now added to RPL+ (see post below), here's a more compact way to write the function above, under utilization of the MorphEngine split function.
Code: Select all
« split NUM »
(split splits a string into a vector of its characters, and NUM is run on each element)
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

Array extension to Hashtable/Set

Postby oliver » Fri May 20, 2011 2:14 pm

Hashtables (sometimes also called sets) are universally useful.

RPL+ will support them via overloading of the []-operator and a mode command that enables their use.
Both numerical and non-numerical keys may be supported. (Still under investigation.)
An array can thereby be turned into a sparse array or hashtable.

In normal array mode, RPL+ will throw an error, if an index outside the defined range is provided to the []- and =[]-operators.
In hashtable mode, you'll be able to specify any index.

This, of course, has the potential to create "holes". If, for example, you assign elements 1, 2, and 5 of a vector, then 3 and 4 are undefined.
If such a hashtable is returned to the stack, or used as an arg to another function, undefined fields will show as data type undefined, which is coerced to false or NaN, depending on use.

A usage example will be solution #14 in the Project Euler shared folder. Its efficiency will be increased by caching chain counts for upcoming path starting numbers vs. only prior ones.

The internal representation will be sparse.
For example, if you do
Code: Select all
1 =a[1]
2 =a[1000000000000000]

this will not create a huge array internally.

The issue of arbitrary key objects (for example, strings) as indices will be clarified.
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

More RPL+ example code

Postby oliver » Fri May 20, 2011 2:21 pm

If you're looking for more RPL+ code examples, check out the shared folder Project Euler, which provides most solution programs in RPL+.
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

Link to Project Euler RPL+ solutions

Postby oliver » Tue Jul 26, 2011 3:50 am

You can now view the RPL+ sources for Project Euler solutions here: http://naivedesign.com/ND1/Project_Euler.html
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

Vector and lists unified; implied MAPping

Postby oliver » Tue Jul 26, 2011 4:01 am

Pulling a longstanding feature from ND1, RPL+ now requires vectors ( [...] ) and lists ( {...} ) to be unified.

That means, there's no longer a difference between the two, and they become, in fact, one single "array" data type.
This implies that historic "list operations" work on vectors, and vice versa.

For example, list processing functions (DOSUBS, DOLIST, SEQ, etc.) accept vectors as input. ∑LIST, modern name total, etc. operate on vectors.

Also, an associated feature is now made part of RPL+: implied "mapping" when a single-valued function is applied to a vector.
For example,
Code: Select all
[1 2 3 4] sin
is permitted and will simply apply sin to each element of the given vector.

While {} (historically used as list delimiters) may still be used in RPL code, this syntax is really equivalent to using [] now.
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

BREAK

Postby oliver » Tue Sep 13, 2011 7:53 am

An odd omission in RPL, in comparison to most other languages, is the lack of a break statement to prematurely terminate loops.

This will be rectified in RPL+ shortly. I'm debating whether to make it simply BREAK, or BREAKIF, where latter would be shorthand for IF THEN BREAK END. That is, BREAKIF would take a condition arg and break, eliminating the need for an IF-construct. (With the added benefit of faster processing through the byte-code compiler.)

If you have an opinion on this, feel free to feed in.
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

Re: BREAK

Postby oliver » Mon Oct 17, 2011 1:12 pm

Further to the last topic, I resolved to add the following to RPL+:

BREAK and IFTB ("if then break")
CONTINUE and IFTC ("if then continue")

These new commands work just like they do in most (C-like) programming languages:

Code: Select all
BREAK

Prematurely exits a containing FOR or START-loop.

Code: Select all
IFTB

Uses the topmost stack item to decide whether to break, or not. If the topmost item evaluates to "true", a break is performed.

Code: Select all
CONTINUE

Completes one iteration of an enclosing FOR or START-loop, and either moves program execution back to the first statement within the loop, or exits (if this was the last iteration).

Code: Select all
IFTC

Uses the topmost stack item to decide whether to continue, or not. If the topmost item evaluates to "true", a continue is performed.
oliver
Site Admin
 
Posts: 433
Joined: Sat May 01, 2010 2:11 pm

Next

Return to RPL / RPL+

Who is online

Users browsing this forum: No registered users and 1 guest

cron