Loop - ObjectVision/GeoDMS GitHub Wiki
MetaScript functions loop
- loop(template, number)
loop(template, number) results in a set of instantiated templates: number instantiations of template, named iter0, iter1, ..., each one fed with the result of the one before it, and a container lastIter that refers to the last of them.
The number argument is a uint16 parameter. All number iterations are instantiated.
Iteration i instantiates the template with two case parameters, bound in the order in which they are configured in the template: the iteration number, as a uint16 value, and the nextValue container of iteration i − 1. The first iteration receives only the iteration number. So the template needs a uint16 parameter, a currValue container as the second case parameter, and a nextValue container that holds what the next iteration starts from.
Earlier versions of this page described an optional stop condition, a boolean parameter that would end the loop early. The engine has never had one: a parameter with that name is an ordinary item of the iteration, and every iteration up to number is instantiated.
The loop function is used for dynamic modelling. See also iterate, which chains one instantiation per name of a string attribute and takes the first iteration's input as an argument.
template LoopTemplate
{
parameter<uint16> NrIter;
container currValue;
container nextValue;
container results
{
parameter<uint16> LoopWaarde := NrIter;
}
}
container loop := loop(LoopTemplate, uint16(5));
The result has containers loop/iter0 to loop/iter4, and loop/lastIter/results/LoopWaarde is 4.
The loop function fixes the names of the iterations, the arguments each one receives and the item (nextValue) that carries the state from one iteration to the next. The same unrolled chain can be configured with for_each_ne over an iteration domain unit, with the names, the arguments and the carried state under the control of the configuration. This is the recommended way to configure a loop with more than one item to carry over, or where the iterations need names or arguments of their own:
container StartingState
{
attribute<float32> stock (Region) := const(100f, Region);
}
template TimeStep
{
// begin case parameters
container PrevState;
// end case parameters
container ResultingState
{
attribute<float32> stock (Region) := PrevState/stock * 1.1f;
}
}
unit<uint32> Iter := range(uint32, 0, nrIter)
{
attribute<string> name := 'Iter' + string(id(.));
attribute<string> PrevName := MakeDefined(name[sub_or_null(id(.), 1)] + '/ResultingState', 'StartingState');
}
container Iters := for_each_ne(Iter/name, 'TimeStep(' + Iter/PrevName + ')');
container LastIter := = last('Iters/' + Iter/name);
Iter/PrevName names, for each iteration, the state it starts from: the ResultingState of the previous iteration, and for the first one, where sub_or_null yields null and the lookup with it too, the value that MakeDefined substitutes, StartingState. The for_each_ne then instantiates TimeStep once per iteration, with that state as its case parameter, and LastIter refers to the last instantiation through an indirect expression. The template carries over whatever ResultingState contains, here one attribute, and as many items as the model needs.
A worked example of this pattern, with a second case parameter and a template call inside the iteration, is the convex hull example script.