Loops
for runs its body once per element of a list or once per number in a range. The number of iterations is fixed when the loop starts, until ends it early, and bound caps it. There is no while.
loops.lithe
(* Loops. The number of iterations is fixed when the loop starts: a range, or a list's
length. `until` ends a loop early. `bound n` caps the count, and a longer list panics. *)
import "std/evm";
import "std/list";
interface Exchange is {
function swap(amount : uint) : uint;
}
event Paid of record {
indexed to : address;
amount : wei;
}
storage is record {
total : uint;
gcd : uint;
found : uint;
members : list(address);
isMember : map(address, bool);
exchange : Exchange;
results : map(uint, uint);
paid : uint;
}
(* A range is half-open: 0 <= i < n. *)
external function triangle(const n : uint) is {
var acc : uint := 0;
for i : uint in 0 .. n do {
acc := acc + i;
}
storage.total := acc;
}
(* A fixed list's length is in its type. `(i, x)` binds the index too. *)
external function weighted(const xs : list(uint, 3)) is {
var acc : uint := 0;
for (i, x) : (uint, uint) in xs do {
acc := acc + x * (i + 1);
}
storage.total := acc;
}
external function join(const who : address) is {
List.push(storage.members, who);
}
(* A storage list can grow, so its loop carries a bound. Past it, the call panics. *)
external function markAll() is {
for who : address in storage.members bound 4 do {
storage.isMember[who] := true;
}
}
(* `until` is checked before each iteration, so the search stops once it has found. *)
external function find(const target : address) is {
var hit : uint := 0;
for (i, who) : (uint, address) in storage.members until hit <> 0 bound 4 do {
if who = target then {
hit := i + 1;
}
}
storage.found := hit;
}
(* A tuple assignment steps two `var`s at once. *)
external function euclid(var a : uint; var b : uint) is {
for _ : uint in 0 .. 256 until b = 0 do {
(a, b) := (b, a % b);
}
storage.gcd := a;
}
(* Effects in a loop: one event and one deferred transfer per recipient. *)
external function pay(const tos : list(address, 2); const amounts : list(uint, 2)) is {
for (i, to) : (uint, address) in tos do {
const amount : wei := wei(amounts[i]);
storage.paid := storage.paid + amounts[i];
emit Paid { to = to; amount = amount; };
defer Evm.transfer(to, amount);
}
}
external function setExchange(const exchange : Exchange) is {
storage.exchange := exchange;
}
(* A continuation in a loop sees that iteration's bindings. *)
external nonreentrant function swapAll(const amounts : list(uint, 2)) is {
for (i, amount) : (uint, uint) in amounts do {
defer storage.exchange.swap(amount) then (out : uint) {
storage.results[i] := out;
};
}
}
external function grid(const n : uint) is {
var acc : uint := 0;
for i : uint in 0 .. n do {
for j : uint in 0 .. n do {
acc := acc + i * j;
}
}
storage.total := acc;
}
external view function total() : uint is
storage.totaltrianglecounts a half-open range:0 .. nruns for0up ton - 1.weightedwalks a fixed list with its index. A fixed list's length is in its type, so the loop's cost is known when the contract is compiled.markAllwalks a storage list, which can grow. Itsbound 4makes the worst case visible, and a fifth member makes the call revert withPanic(0x01). Leave the bound off and the compiler warns that the loop's gas depends on storage.findstops as soon asuntilis true. The condition is checked before every iteration, including the first.euclidtakesvarparameters and steps them together with a tuple assignment. Withb = 0the loop runs zero times.payandswapAllshow effects in a loop: one event and one deferred call per iteration, in order, and a continuation that sees its own iteration'si.- Maps cannot be iterated. Keep an iterable set when members must be listed.

