среда, 20 января 2016 г.

#869. :, PROCEDURE, FUNCTION. Parameters at the left and at the right. Part 2

Original in Russian: http://programmingmindstream.blogspot.ru/2015/12/1167-procedure-function-2.html

The previous series was here: http://18delphi.blogspot.ru/2016/01/868-procedure-function-parameters-at.html.

The key words :, ;, FUNCTION, PROCEDURE and “parameters at the left” were considered there.

Now, let’s discuss the “parameters at the right”.

Suppose, we have an example with “parameters at the left”:

INTEGER FUNCTION Plus
 INTEGER IN A
 INTEGER IN B
 A B + >>> Result // adds A to B and write to Result
; // Plus
 
1 2 Plus . // calls the function and prints the result

This is a case of “typical” RPN.

How can we use infix notation?

This is where parameters at the right will help.

We rewrite the example using parameters at the right:

INTEGER FUNCTION Plus
 INTEGER IN A // parameter at the left
 ^ IN B // parameter at the RIGHT is passed by REFERENCE rather than by VALUE.
        //   It has to be dereferenced.
 A // value of parameter A
 B DO // dereferences the value B, i.e. calls the DO method on the word specified by B
 + >>> Result // adds A to B and write to Result
; // Plus
 
1 Plus 2 . // calls the function “INFIXELY” and prints the result

Note that parameters at the right are passed by reference.

We can also write:

1 Plus ( 2 Plus ( 3 Plus 4 ) ) .

The brackets is a must for now.

I’ll write how to do without them in a special article.

We can rewrite the example as follows:

INTEGER FUNCTION Plus
 INTEGER IN A // parameter at the left
 ^ IN B // parameter at the RIGHT is passed by REFERENCE rather than by VALUE.
        //   It has to be dereferenced.
 A // value of parameter A
 B |^ // dereferences the value B, i.e. calls the |^ method on the word specified by B
 + >>> Result // adds A to B and writhes to Result
; // Plus
 
1 Plus 2 . // calls the function “INFIXELY” and prints the result

|^ is used instead of DO here.

They are equal actually.

A bit later, I will write about the differences.

The |^ method is defined in axiomatic as follows:

: |^
  ^@ IN aRef
   
 %SUMMARY 'Dereferences the parameter at the left' ;
 aRef pop:Word:GetRef DO
; // |^

The details of  |^ implementation will also be described later.

Now, I will note that |^ uses DO, i.e. |^ is derived from DO.

Let’s go on.

Why are the parameters at the right passed by reference rather than by value?

There are many reasons, in particular "lazy evaluations".

Let’s look at the implementation of the Boolean operation AND and OR:

BOOLEAN operator AND
  BOOLEAN IN aFirst
  ^ IN aSecond
 %SUMMARY 'Double-sided rather than reverse polish &&' ;
 if aFirst then
  (
   if ( aSecond DO ) then
    ( true >>> Result )
   else
    ( false >>> Result )
   )
 else
  ( false >>> Result )
; // AND
 
BOOLEAN operator OR
  BOOLEAN IN aFirst
  ^ IN aSecond
 // Double-sided rather than reverse polish  ||
 if aFirst then
  ( Result := true )
 else
  if ( aSecond DO ) then
   ( Result := true )
  else
   ( Result := false )
; // OR

As we can see, the parameter aSecond is calculated ONLY in case we need to calculate the whole expression.

It means that the result of expression is not clear on the parameter aFirst.

The word operator is equal to the words : and FUNCTION. It merely stresses the words are “operator-related”.

In particular, operators can set "order of operations" as in Prolog, for example, to get rid of the brackets in the example with Plus (see above).

I’ll tell about it later.

Now, let us believe the operator is specified as follows:

WordAlias operator :
WordAlias OPERATOR :

What do we get using lazy evaluation?

Without lazy evaluation:

if ( ( anObject <> nil ) ( anObject .SomeMethod ) && ) then

we get Access Violation.

With lazy evaluation:

if ( ( anObject <> nil ) AND ( anObject .SomeMethod ) ) then

we get no Access Violation.

Hope you see why.

The operation <> is, by the way, defined in base axiomatic using parameters at the right and at the left and the operation =.

In this way:

BOOLEAN operator <>
  IN aLeft
  ^ IN aRight
 %SUMMARY 'Right-sided rather than reverse polish !=' ;
 Result := ( aLeft = ( aRight DO ) ! )
; //<>

No comments. Note that the operation ! is a postfix denial.

Let’s go on.

The fact that the reference to the word rather than value is passed means that, if a variable is given as the word, we can write to it.

Let us implement the examples of increment and decrement methods as in axiomatic:

VOID operator DEC
  ^ IN aWhatToDecrement
 aWhatToDecrement DO // dereferences the variable aWhatToDecrement
 1 - // decrement by 1
 >>>^ aWhatToDecrement // writes the value as pointed by the aWhatToDecrement
; // DEC
 
VOID operator INC
  ^ IN aWhatToIncrement
 aWhatToIncrement DO // dereferences the variable aWhatToDecrement
 1 + // increment by 1
 >>>^ aWhatToIncrement // writes the value as pointed by the aWhatToIncrement
; // INC
The call: 
INTEGER VAR A // specifies the integer variable A
0 >>> A // initializes it using zero
A . // prints
INC A // increases A by 1
A . // prints
DEC A // decreases A by 1
A . // prints

Sure, if we write Inc 1 we get compilation error, if not particularly run-time error.

Suppose also that we need to describe the methods IncBy and DecBy.

Here they are:

VOID operator DecBy
  ^ IN aWhatToDecrement
  ^ IN aDelta
 aWhatToDecrement DO // dereferences the variable aWhatToDecrement
 aDelta DO // dereferences the variable aDelta
 - // substraction
 >>>^ aWhatToDecrement //  writes the value as pointed by the aWhatToDecrement
; // DecBy
 
VOID operator IncBy
  ^ IN aWhatToIncrement
  ^ IN aDelta
 aWhatToIncrement DO // dereferences the variable aWhatToDecrement
 aDelta DO // dereferences the variable aDelta
 + // addition
 >>>^ aWhatToIncrement //  writes the value as pointed by the aWhatToIncrement
; // IncBy

The call:

INTEGER VAR A // specifies the integer variable A
0 >>> A // initializes it using zero
A . // - печатаем
IncBy A 2 // increases A by 2
A . // prints
DecBy A 2 // decreases A by 2
A . // prints

Let’s move on.

parameters at the right are also useful when dealing with lambda expressions.

Here is an example:

: Iteration
  ^ IN aLambda
 0 // initial value
 1 aLambda DO
 2 aLambda DO
 3 aLambda DO
 4 aLambda DO
 5 aLambda DO
 6 aLambda DO
 7 aLambda DO
 8 aLambda DO
 9 aLambda DO
 10 aLambda DO
; // Iteration
 
// The call:
 
Iteration ( IN A IN B A B + ) . // sums numbers from 0 to 10 and prints the result
 
// a shorter version:
 
Iteration + . // sums numbers from 0 to 10 and prints the result

The initial value can be factored out:

: Iteration
  ^ IN aLambda
 1 aLambda DO
 2 aLambda DO
 3 aLambda DO
 4 aLambda DO
 5 aLambda DO
 6 aLambda DO
 7 aLambda DO
 8 aLambda DO
 9 aLambda DO
 10 aLambda DO
; // Iteration
 
// The call:
 
0 Iteration ( IN A IN B A B + ) . // sums numbers from 0 to 10 and prints the result
 
// a shorter version:
 
0 Iteration + . // sums numbers from 0 to 10 and prints the result
 
1 Iteration * . // multiplies numbers from 1 to 10 and prints the result

Arrays and iteration (in Russian) can be used for them: 
  ^ IN aLambda
 [ 1 2 3 4 5 6 7 8 9 10 ] .for> ( aLambda DO )
; // Iteration
 
// The call:
 
0 Iteration ( IN A IN B A B + ) . // sums numbers from 0 to 10 and prints the result
 
// a shorter version:
 
0 Iteration + . // sums numbers from 0 to 10 and prints the result
 
1 Iteration * . // multiplies numbers from 1 to 10 and prints the result

Let us sum up.

Parameters at the right and dereferencing were analyzed.

We also considered writing values to the variables the parameters at the right point to and how parameters at the right can be used for lambda expressions.

We scraped the surface of arrays and iteration through arrays.

In the next article, the parameters at the right passed by reference will be discussed along with the way of implementing the operations like += -= and so on.

Hope the article was of some use for you.

Комментариев нет:

Отправить комментарий