Jump to content

User:Marc Schroeder/sandbox

From Wikipedia, the free encyclopedia

LOOP is a simple register language designed to precisely capture the primitive recursive functions.[1] The language is derived from the counter-machine model.[2] Like the counter machines the LOOP language comprises a set of one or more unbounded registers,[3] each of which can hold a single non-negative integer. A few arithmetic instructions (like 'Increment', 'Clear', 'Copy', 'Decrement', ...) operate on the registers. The only control flow instruction is LOOP x { ... }. It causes the instructions within its scope to be repeated times.[4]

In contrast to GOTO programs and WHILE programs, LOOP programs always terminate,[5]. The running time is known in advance. Therefore, the set of functions computable by LOOP-programs is a proper subset of computable functions (and thus a subset of the computable by WHILE and GOTO program functions).[6]

History[edit]

The LOOP language was formulated in a 1967 paper by Albert R. Meyer and Dennis M. Ritchie. They showed the correspondence between the LOOP language and primitive recursive functions, proving that each primitive recursive function is LOOP-computable and vice versa.[7]

The language also was the topic of the unpublished PhD thesis of Ritchie.[8][9]

It was presented by Uwe Schöning, along with GOTO and WHILE.[10]

An example of a total computable function that is not LOOP computable is the Ackermann function.[11]

Definition[edit]

Syntax[edit]

A LOOP program consists of

  1. a finite number of registers,[12] any of which may contain an arbitrary integer of ;
  2. a sequence of instructions as specified below.

The set of all LOOP programs L is defined by the following rules:

  • Each of the arithmetic instructions, as well as the empty program, are elements of L.[13]
  • If L and x is a register, then the program
LOOP x:
    P
belongs to L.
  • If L and L, then the program
P
Q
belongs to L.

Formal definition

Compare with Python grammar

The specification below — in EBNF — uses whitespace to delimit control flow blocks (following the off-side rule).

  • The symbol "::=" serves the same purpose as colon in Bison.
  • Parentheses group.
  • A trailing asterisk (*) indicates 0 or more repetitions.
  • A trailing plus indicates 1 or more repetitions.
program ::= instruction*

instruction ::= 
    (arithmetic_instruction | loop_instruction) NEWLINE

arithmetic_instruction ::=
      inc_instruction        /* increment */
    | clr_instruction        /* clear     */
    | cpy_instruction        /* copy      */
    | dec_instruction        /* decrement */

inc_instruction ::= register "++"
clr_instruction ::= register "= 0"
cpy_instruction ::= register "=" register
dec_instruction ::= register "--"

loop_instruction ::= "loop" register ":" NEWLINE INDENT instruction+ DEDENT

register ::= char (char | digit)* 
char     ::= "A" | "B" | ... | "Z" | "_" | "a" | "b" | ... | "z"
digit    ::= "0" | "1" | ... | "9"

input registers are specified immediately above the program:

output registers are specified immediately below the program:

Semantics[edit]

Each register of a LOOP program may contain an arbitrary integer of .

The instructions are executed sequentially in the order in which they occur. The instruction LOOP x { ... } causes the included code to be executed times in succession, where denotes the contents of the register x upon entering the loop.[4]

Definition

A LOOP program P is said to compute a function , when

  1. , considered as an m-tuple, specifies registers which contain the arguments of ; the remaining registers contain ;
  2. , considered as an n-tuple, specifies registers;
  3. after the execution of P the output registers contain .[14]

Remarks

  • When specifies a single register the program defines a scalar-valued function. Otherwise the program defines a vector-valued function.[15][16][17]
  • Primitive recursive functions have the signature , i.e. they map n arguments to a scalar value. LOOP-programs define exactly the primitive recursive functions.[7] By consequence a LOOP-program executes a primitive recursive function iff specifies exactly one register.
  • As any list of registers may be designated as input and output, in general a LOOP program can compute several functions, depending on the given mapping.[18]
  • It is natural to extend the notion of primitive recursiveness to vector-valued functions.
Definition: A (vector-valued) function is primitive recursive iff it can be computed by a LOOP program.[19]


Remark on the choice of arithmetic instructions The LOOP language has been defined by many authors.[20][21] The definitions do not agree on the arithmetic instructions. The minimum requirement is that it must be possible to increment registers and to set them to zero. This is achieved by the instructions xi := xi + 1, and either xi := 0 or xi := xj.[22]

From the instructions (1) and (2) the remaining instructions can be derived.

From the instructions (1) and (3) the remaining instructions can be derived.

No subroutines, but ...[edit]

Let P be any LOOP program.

Let be a primitive recursive function. Then is computed by some LOOP program Q.

If P and Q do not share registers, Q can be inserted anywhere into P such that the programs do not affect each other.

The operation to rename the registers of Q is called alpha-conversion. This results in the program Q'.

Assume that P and Q' share no registers.

// INPUT (pi1, pi2, ..., pim)
Q'
// OUTPUT (po1, po2, ..., pon)

Wherever n registers of P need to be updated with the output of Q, given m registers of P as input, one can insert Q' into P and connect the input/output of Q' to the relevant registers of P. The resulting code is

pi1 = some_Qregister_1;
pi2 = some_Qregister_2;
...
pim = some_Qregister_m;
// clear all remaining registers of P'
P'
qo1 = po1;
qo2 = po2;
...
qom = po2;
(qo1, qo2, ..., qon) = P(qi1, qi2, ..., qim)


One can allow as an extra arithmetic instruction the instruction

(y1, y2, ..., yn) = P(x1, x2, ..., xm)

to indicate that the LOOP program has computed f on the side, and copied the output to the registers (y1, y2, ..., yn).

This is a short-hand notation to indicate that


Machtey (1971) added to the LOOP language the extra instruction Let P and Q be LOOP programs. can be patched into another loop program. The following workaround is possible.


Then P can be patched into a LOOP program Q, provided that

  • (to avoid name collision) the local registers of P and m new registers (like tmpx1, tmpx2, ..., tmpxm) are not mentioned in Q;
  • before the first line of P:
    • the registers x1, x2, ..., xm are backed up to tmpx1, tmpx2, ..., tmpxm respectively;
    • each register y1, y2, ..., yn is initialised to 0, if not mentioned in INPUT list;
    • each local register of P is initialised to 0;
  • after the last line of P:
    • the registers x1, x2, ..., xm are restored from tmpx1, tmpx2, ..., tmpxm respectively.

This simulates the behaviour of an activation record, as far as needed here.

We write

...
(y1, y2, ..., yn) = S (x1, x2, ..., xm)
...

Example Programs[edit]

Addition[edit]

Addition is the hyperoperation function .

can be implemented by the LOOP program ADD( x, y )

# INPUT <x, y>
LOOP y:
    x = x + 1
# OUTPUT <x>
INPUT <x, y>
  ├─────────────┐
┌─┴───────┐    ╔╧╗
│x = x + 1│    ║y║
└─┬───────┘    ╚╤╝
  ├─────────────┘
OUTPUT <x>

          │
         ╔╧╗
         ║x║
         ╚╤╝

Assignment x = y without using type 3 instruction[edit]

If in the previous program — ADD — the register x is initialized to 0, the program copies the contents of register y to register x.

# INPUT <x, y>
x = 0
LOOP y:
    x = x + 1
# OUTPUT <x>

As the example shows the assignment instruction (x = y) can be removed from the list of arithmetic instructions without loss of expressive power.

Clearance x = 0 without using the type 2 instruction[edit]

{{{1}}}

Multiplication[edit]

Multiplication is the hyperoperation function .

can be computed by the LOOP program MULT( x, y )

# INPUT <x, y>
LOOP x:
    z = ADD( z, y )
# OUTPUT <z>

The program uses the ADD() program as a "convenience instruction". Expanded, the MULT program is a LOOP-program with two nested LOOP instructions. ADD counts for one.

More hyperoperators[edit]

Given a LOOP program for a hyperoperation function , one can construct a LOOP program for the next level.

for instance — exponentiation — can be implemented by the LOOP program POWER( x, y )

# INPUT <x, y>
z = z + 1
LOOP y:
    z = MULT( z, x )
# OUTPUT <z>

The exponentiation program, expanded, has three nested LOOP instructions.

Remark: To use MULT as a subroutine one has to take care of the clash of the variable z, which must have initial value 0 in MULT.

Predecessor[edit]

The predecessor function is defined as

.

is computed by the built-in arithmetic 'dec_instruction' . This instruction could be dropped, as can be derived from 'cpy_instruction' and 'inc_instruction' .[23][24]

# INPUT (x)
LOOP x:
|   x = a
|   a++
# OUTPUT (x)

can also be derived from 'inc_instruction' and 'clr_instruction' .

# INPUT (x)
FirstTime++
LOOP x:
|   x++
|   LOOP FirstTime: 
|   |   FirstTime = 0
|   |   x = 0
# OUTPUT (x)

Remarks:

  • The reason to keep 'dec_instruction' as an arithmetic instruction consists in the fact that cannot be programmed without wasting a LOOP in situations where a low LOOP nesting does matter.
  • If OUTPUT is set to (FirstTime) instead of to (x) the program computes the function
.
INPUT <x>
  ├───────────────────┐
┌─┴───────┐1          │
│x = 0    │           │
└─┬───────┘           │
  ├─────────────┐     │
┌─┴───────┐2   ╔╧╗   ╔╧╗
│x = x + 1│    ║a║   ║x║
└─┬───────┘    ╚╤╝   ╚╤╝
  ├─────────────┘     │
┌─┴───────┐3          │
│a = a + 1│           │
└─┬───────┘           │
  ├───────────────────┘
OUTPUT <x>

This program can be used as a subroutine in other LOOP programs. The LOOP syntax can be extended with the following statement, equivalent to calling the above as a subroutine:

x0 := x1 ∸ 1

Remark: Again one has to mind the side effects, see above

Direct sum of 2x3 matrix and 2x2 matrix[edit]

The direct sum (denoted by ) of any pair of matrices A of size 2 × 3 and B of size 2 × 2 is a matrix C of size (2 + 2) × (3 + 2), defined as [25][26]

[27]

can be computed by an empty LOOP-program. The selection and the order of the input/output registers does the trick.

The 10-tuple < x0, x1, x2, x3, x4, x5, x6, x7, x8, x9 > is designated as INPUT.

The input registers are associated with the matrices and as follows:

The 20-tuple < x0, x1, x2, x10, x11, x3, x4, x5, x12, x13, x14, x15, x16, x6, x7, x17, x18, x19, x8, x9 > is designated as OUTPUT.

The output registers are associated with the matrix as follows:

Remarks:

  • Recall that a primitive recursive function maps its arguments to a scalar value. . Considering that LOOP-programs execute exactly the primitive recursive functions, it is natural to extend this notion to vector-valued functions by the following

Definition: A (vector-valued) function is primitive recursive iff it can be implemented by a LOOP program. [28]


Cut-off subtraction[edit]

If in the 'addition' program above the second loop decrements x0 instead of incrementing, the program computes the difference (cut off at 0) of the variables x1 and x2.

x0 := x1
LOOP x2 DO
  x0 := x0 ∸ 1
END
  1.   x0 = x1
  2.   LOOP x2 DO
  3.           x0 = x0 ∸ 1
  4.   END
# INPUT <X0, x1>
x0 = x1
LOOP x2:
    x0 = x0  1
# OUTPUT <X0>


Remark

INPUT ⟨ x, y ⟩
LOOP y DO
  a := 0;
    LOOP x DO
      x := a;
      a := a + 1;
  END
END
OUTPUT ⟨ x ⟩

Like before we can extend the LOOP syntax with the statement:

x0 := x1 ∸ x2

If – then[edit]

The equality of two variables and is tested with the function

.

This function can be computed by the LOOP program

# INPUT (x, y)
eq++
difference = x
LOOP y:
    difference--
LOOP difference:
    eq = 0
difference = y
LOOP x:
    difference--
LOOP difference:
    eq = 0
# OUTPUT (eq)

The control flow instruction ' if (x == y) then P' can then be simulated with

# do zero times or one time
LOOP eq:
    P

The steepest functions[edit]

Since the initial functions are all dominated by the successor function, iterations of it dominates all functions defined by iterations from the initial functions.[30] Let be the (series of) functions

where is the x-th iterate of .[31]

Consider any LOOP program P of length . Then the function [32] dominates whatever function P might compute.

Also, on input , P halts after the executions of at most arithmetical imstructions. As LOOP programs have finite length, it follows that the generalized function , an Ackermann function, cannot be computed by a LOOP program. This is a proof that the function is not primitive recursive.

Example: the function is computed by the following program of lenght 5:

# INPUT (x)
LOOP x:
|   LOOP x: 
|   |   LOOP x:
|   |   |   LOOP x: 
|   |   |   |   x++
# OUTPUT (x)

On input no other LOOP program of length at most 5 can output a bigger number.

If the loop nesting is taken into account, then a more advantageous bounding function can be established.

Let .

Then dominates whatever function any LOOP program P of length and maximal loop nesting might compute. The idea is that the program is chopped up into c subprograms each of which races to the max. Each subprogram takes as input the output of the preceding subprogram.

Example: If and then .

# INPUT (x)
LOOP x:
|   LOOP x: 
|   |   x++
LOOP x:
|   LOOP x: 
|   |   x++
LOOP x:
|   LOOP x: 
|   |   x++
# OUTPUT (x)

The structured program theorem[edit]

Consider the pseudocode listed in the strucured program article.

See Machtey 1972 and Calude 1988, p. 402, (Universal Language).

Marvin Minsky [33][34] defined the following simple counter machine model, which is Turing complete. The instructions are

INC ( r ): INCrement the constents of register r
DEC ( r ): DECrement the constents of register r
JZ ( r, z ): IF register r contains Zero THEN Jump to instruction z ELSE continue in sequence.

A so-called Program machine consists of a finite sequence of n instructions. It can be simulated by a while-program of the form

p = 1
while p != 0:
    if p == 1:
        p = 2 
        if instruction 1 is of type JZ(r,z) and r == 0:
            p = z
        else:
            perform instruction 1

    if p == 2:
        p = 3
        if instruction 2 is of type JZ(r,z) and r == 0:
            p = z
        else:
            perform instruction 2

    (...)

    if p == n:
        p = 0
        if instruction n is of type JZ(r,z) and r == 0:
            p = z
        else:
            perform instruction n

This rewriting procedure actually proves the folk version of the structured program theorem by [Bohm Jacopini, 1966].

The LOOP-language does not have the while-instruction. The replacement of the while-instruction by the instruction LOOP t results in a LOOP-program which does the same as the while-program but stops after t iterations.

Meyer & Ritchie (1967) proved that ... xxx HIER VERDER 2

See also[edit]

Notes and references[edit]

  1. ^ Enderton 2012.
  2. ^ Cutland 1980, p. 9.
  3. ^ variables
  4. ^ a b Changes of the content of register x during the execution of the loop do not affect the number of passes.
  5. ^ Schöning 2008, p. 93.
  6. ^ Schöning 2001, p. 122.
  7. ^ a b Meyer & Ritchie 1967.
  8. ^ "Discovering Dennis Ritchie's Lost Dissertation". CHM. 2020-06-19. Retrieved 2020-07-14.
  9. ^ "Program structure and computational complexity draft | 102790971 | Computer History Museum". www.computerhistory.org. Retrieved 2020-07-14.
  10. ^ Schöning 2008, p. 105.
  11. ^ Schöning 2008, p. 112.
  12. ^ Register names are made up of letters, digits and underscores "_"; the first character must be a letter.
  13. ^
    The arithmetic instructions are (x and y being any register names):
    1 x := x + 1 Increment contents of register x inc x
    2 x := 0 Clear register x clr x
    3 x := y Copy contents of register y to register x x y
    4 x := x ∸ 1 Decrement contents of register x dec x
  14. ^ We write .
  15. ^ Fachini & Maggiolo-Schettini 1979.
  16. ^ Matos 2015.
  17. ^ PlanetMath.
  18. ^ Goetze & Nehrlich 2015, p. 261.
  19. ^ The definition given at PlanetMath describe the same functions
  20. ^ Matos 2015, p. 66.
  21. ^ Matos 2014.
  22. ^ xi can be overwritten with a register xj holding the value 0 throughout the program.
  23. ^ Matos 2015, p. 68, Example 2.
  24. ^ See also Meyer & Ritchie 1967, p. 466, (2.2).
  25. ^ Lipschutz & Lipson 2017.
  26. ^ See the article 'Matrix addition'.
  27. ^ For instance,
  28. ^ The definition given at PlanetMath describe the same functions
  29. ^ Matos 2015, p. 68.
  30. ^ Odifreddi & xxx, p. 298.
  31. ^
  32. ^ is highest value amongst the input registers of P
  33. ^ Minsky 1961, p. 449.
  34. ^ Minsky 1967, p. 200.

Cite error: A list-defined reference named "FOOTNOTEMeyerRitchie1967" is not used in the content (see the help page).
Cite error: A list-defined reference named "FOOTNOTEFachiniMaggiolo-Schettini197959" is not used in the content (see the help page).
Cite error: A list-defined reference named "FOOTNOTESchöning2008" is not used in the content (see the help page).

Cite error: A list-defined reference named "FOOTNOTEMatos2015" is not used in the content (see the help page).

Bibliography[edit]

  • Fachini, Emanuela; Maggiolo-Schettini, Andrea (1982). "Comparing Hierarchies of Primitive Recursive Sequence Functions". Zeitschrift für mathematische Logik und Grundlagen der Mathematik. 28 (27–32): 431–445. doi:10.1002/malq.19820282705.
  • Lipschutz, Seymour; Lipson, Marc (2017). Schaum's Outline of Linear Algebra (6 ed.). McGraw-Hill Education. ISBN 9781260011449.
  • Schöning, Uwe (2001). Theoretische Informatik-kurz gefasst (4 ed.). London: Oxford University Press. ISBN 3-8274-1099-1.

External links[edit]

Category:Computability theory