ligo/gitlab-pages/docs/language-basics/loops.md
2020-02-05 16:28:40 +01:00

232 lines
7.0 KiB
Markdown

---
id: loops
title: Loops
---
## General Iteration
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
General iteration in PascaLIGO takes the shape of "while" loops, which
should be familiar to programmers of imperative languages. Those loops
are of the form `while <condition> <block>`. Their associated block is
repeatedly evaluated until the condition becomes true, or never
evaluated if the condition is false at the start. The loop never
terminates if the condition never becomes true. Because we are writing
smart contracts on Tezos, when the condition of a "while" loops fails
to become true, the execution will run out of gas and stop with a
failure anyway.
Here is how to compute the greatest common divisors of two natural
number by means of Euclid's algorithm:
```pascaligo group=a
function gcd (var x : nat; var y : nat) : nat is block {
if x < y then
block {
const z : nat = x;
x := y; y := z
}
else skip;
var r : nat := 0n;
while y =/= 0n block {
r := x mod y;
x := y;
y := r
}
} with x
```
You can call the function `gcd` defined above using the LIGO compiler
like so:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/loops/gcd.ligo gcd '(2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)'
# Outputs: +12
```
<!--CameLIGO-->
CameLIGO is a functional language where user-defined values are
constant, therefore it makes no sense in CameLIGO to feature loops,
which we understand as syntactic constructs where the state of a
stopping condition is mutated, as with "while" loops in PascaLIGO.
Instead, CameLIGO features a *fold operation* as a predefined function
named `Loop.fold_while`. It takes an initial value of a certain type,
called an *accumulator*, and repeatedly calls a given function, called
*iterated function*, that takes that accumulator and returns the next
value of the accumulator, until a condition is met and the fold stops
with the final value of the accumulator. The iterated function needs
to have a special type: if the type of the accumulator is `t`, then it
must have the type `bool * t` (not simply `t`). It is the boolean
value that denotes whether the stopping condition has been reached.
```cameligo group=b
let iter (x,y : nat * nat) : bool * (nat * nat) =
if y = 0n then false, (x,y) else true, (y, x mod y)
let gcd (x,y : nat * nat) : nat =
let x,y = if x < y then y,x else x,y in
let x,y = Loop.fold_while iter (x,y)
in x
```
To ease the writing and reading of the iterated functions (here,
`iter`), two predefined functions are provided: `continue` and `stop`:
```cameligo group=c
let iter (x,y : nat * nat) : bool * (nat * nat) =
if y = 0n then stop (x,y) else continue (y, x mod y)
let gcd (x,y : nat * nat) : nat =
let x,y = if x < y then y,x else x,y in
let x,y = Loop.fold_while iter (x,y)
in x
```
You can call the function `gcd` defined above using the LIGO compiler
like so:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/loops/gcd.mligo gcd (2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)'
# Outputs: +12
```
<!--ReasonLIGO-->
ReasonLIGO is a functional language where user-defined values are
constant, therefore it makes no sense in ReasonLIGO to feature loops,
which we understand as syntactic constructs where the state of a
stopping condition is mutated, as with "while" loops in PascaLIGO.
Instead, ReasonLIGO features a *fold operation* as a predefined
function named `Loop.fold_while`. It takes an initial value of a
certain type, called an *accumulator*, and repeatedly calls a given
function, called *iterated function*, that takes that accumulator and
returns the next value of the accumulator, until a condition is met
and the fold stops with the final value of the accumulator. The
iterated function needs to have a special type: if the type of the
accumulator is `t`, then it must have the type `bool * t` (not simply
`t`). It is the boolean value that denotes whether the stopping
condition has been reached.
```reasonligo group=d
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
if (y == 0n) { (false, (x,y)); } else { (true, (y, x mod y)); };
let gcd = ((x,y) : (nat, nat)) : nat =>
let (x,y) = if (x < y) { (y,x); } else { (x,y); };
let (x,y) = Loop.fold_while (iter, (x,y));
x;
```
To ease the writing and reading of the iterated functions (here,
`iter`), two predefined functions are provided: `continue` and `stop`:
```reasonligo group=e
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
if (y == 0n) { stop ((x,y)); } else { continue ((y, x mod y)); };
let gcd = ((x,y) : (nat, nat)) : nat =>
let (x,y) = if (x < y) { (y,x); } else { (x,y); };
let (x,y) = Loop.fold_while (iter, (x,y));
x;
```
<!--END_DOCUSAURUS_CODE_TABS-->
## For Loops
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
To iterate over a range of integers you use a loop of the form `for
<variable assignment> to <upper integer bound> <block>`, which is
familiar for programmers of imperative languages. Note that, for the
sake of generality, the bounds are of type `int`, not `nat`.
```pascaligo group=f
function sum (var n : nat) : int is block {
var acc : int := 0;
for i := 1 to int (n) block {
acc := acc + i
}
} with acc
```
You can call the function `sum` defined above using the LIGO compiler
like so:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/loops/sum.ligo sum 7n
# Outputs: 28
```
<!--END_DOCUSAURUS_CODE_TABS-->
<!--DOCUSAURUS_CODE_TABS-->
<!--Pascaligo-->
PascaLIGO "for" loops can also iterate through the contents of a
collection, that is, a list, a set or a map. This is done with a loop
of the form `for <element var> in <collection type> <collection var>
<block>`, where `<collection type` is any of the following keywords:
`list`, `set` or `map`.
Here is an example where the integers in a list are summed up.
```pascaligo group=g
function sum_list (var l : list (int)) : int is block {
var total : int := 0;
for i in list l block {
total := total + i
}
} with total
```
You can call the function `sum_list` defined above using the LIGO compiler
like so:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_list
'list [1;2;3]'
# Outputs: 6
```
Here is an example where the integers in a set are summed up.
```pascaligo=g
function sum_set (var s : set (int)) : int is block {
var total : int := 0;
for i in set s block {
total := total + i
}
} with total
```
You can call the function `sum_set` defined above using the LIGO compiler
like so:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_set
'set [1;2;3]'
# Outputs: 6
```
Loops over maps are actually loops over the bindings of the map, that
is, a pair key-value noted `key -> value` (or any other
variables). Give a map from strings to integers, here is how to sum
all the integers and concatenate all the strings.
You can call the function `sum_map` defined above using the LIGO compiler
like so:
```shell
ligo run-function
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_map
'map ["1"->1; "2"->2; "3"->3]'
# Outputs: ( "123", 6 )
```
<!--END_DOCUSAURUS_CODE_TABS-->