Skip to content

While

This guide provides an overview and brief instructions on using While blocks to implement workflow loops based on configurable conditions.

Getting started

With the While block, you can perform iterative computations by exchanging data with other blocks in a loop. This block drives a loop whose body may consist of one or more blocks. It performs a condition check, sends request data to the loop body, thereby starting its execution, receives the response data from the loop body, and starts its execution again if the specified condition is met, otherwise the loop ends.

The block is configured by default in such a way that each iteration of the loop begins with a condition check. If the condition is not met at the very first iteration, the block ends without executing the loop body. Otherwise, the block receives the response data from the loop body and starts the next iteration depending on whether the specified condition is met for the received data. The loop ends if the condition for the received data is not met. The following diagram illustrates the operation of the block in the mode described above:

flowchart TB
  begin([Begin]) --> condition{Condition<br>check}
  condition --> |Condition is not met| finish([End loop])
  body --> |Response<br>from loop body| condition
  condition --> |Condition is met<br><br>Request to loop body| body(Loop body execution)

Sometimes the response data from the loop body is required to check the condition on the first iteration. Therefore, the block also provides for unconditional execution of the loop body at the first iteration, after which the condition is checked. The block starts the next iteration depending on whether the specified condition is met for the received data, similar to the previously described mode of operation. The following diagram illustrates the operation of the block with unconditional execution of the loop body at the first iteration:

flowchart TB
  begin([Begin]) --> |Request to loop body| body1(Loop body execution)
  body1 --> |Response from loop body| condition{Condition<br>check}
  condition --> |Condition is not met| finish([End loop])
  body --> |Response<br>from loop body| condition
  condition --> |Condition is met<br><br>Request to loop body| body(Loop body execution)

To select the operating mode, use the Unconditionally run the first iteration toggle in the block configuration dialog. This toggle is off by default, which causes the block to operate in the first mode described above. Turn this toggle on if you need to ensure that the loop body is unconditionally executed on the first iteration.

Block variables and their ports

The data to be checked is passed to the condition using variables. Each variable is associated with four ports whose names are the same as the variable's name:

  • The input port to set the initial value of the variable. The value from this port is assigned to the variable at the start of the block execution.
  • The request port for passing the variable value to the loop body. The port emits this value at the beginning of the iteration.
  • The response port to receive the response value from the loop body. This value is assigned to the variable at the end of the iteration.
  • The output port to emit the variable's value at the end of the loop. The current value of the variable appears on this port at the end of the block execution.

Thus, the value of a variable can change at the following points in time:

  • At the start of the block execution, the variable is assigned the value from its input port.
  • At the end of each iteration of the loop, the variable is assigned the value from its response port, if there is a link connected to that port. Otherwise the variable keeps its initial value from the input port.
    • Note the above does not apply to the built-in variable index: the block increments its value by one at the end of each iteration, and if its response port is connected, it is used to signal that the loop body has finished execution, regardless of the response value (see Built-in ports for details).

The rest of the block operation time the current value of the variable remains unchanged. It is this value that is used when checking the condition.

The While block provides the built-in variable index. Its request port can be used to pass the current iteration number to the loop body. You can create other variables to receive responses and check them against the condition.

Creating variables

To check a loop response against the condition, you need to create a variable. Together with each variable, four ports of the same name are created: an input port, a request port, a response port, and an output port. The condition can be checked for the value received through the variable's input port (on the first iteration) or response port (on subsequent iterations).

The variables are listed in the Variables area of the block configuration dialog. To create a variable, click the button at the top of this area, and then input a name for the new variable and its initial value. The name identifies the variable in the condition expression and is also assigned to the ports associated with the variable. The initial value is assigned to the variable's input port.

Usage example

To clarify the operation of the While block, consider the following example.

Suppose that solving a certain problem requires several iterations - such as successive approximations - performed by a block named Solver, which has a single input and a single output. The input requires the current iteration number. The output sends either True, indicating that the problem has been solved at the current iteration, or False, meaning that more iterations are required.

With the While block you can implement the solving loop as follows:

  • In the While block configuration dialog, create a variable named solved and set its initial value to False.
  • Link the index request port of the While block to the Solver input.
  • Link the Solver output to the solved response port of the While block.
  • Use the value of the solved variable in the condition. The solving loop continues while the condition is met, so the condition expression would be not "solved".

Since the block variables are assigned some initial values, their input ports can be left unconnected, and the loop can be started by sending a signal through a link to the input port @go (see Signal ports).

At each iteration of the loop, the While block passes the current iteration number to the Solver block, causing it to start the next solving iteration. After completing its iteration, the Solver block returns either True or False. If the While block receives False, the condition expression not "solved" evaluates to True (condition met), so the block outputs the next iteration number. The loop continues until Solver outputs True.

Iteration number limit

In the above example, you can use the index variable to limit the total number of iterations. For instance, to set a limit of 100 iterations, add a condition to check that the value of the index variable is less than 100. The condition expression in this case would be not "solved" and "index"⁠<⁠100. Since the index variable value is equal to the current iteration number, the loop is forced to end after the 100-th iteration.

Block setup basics

The fundamental elements of the While block setup are variables with their ports, and the condition expression:

  • Built-in ports - these are the ports of the index built-in variable, whose current value is the iteration number of the loop: the index input specifies the first iteration number, the index request port issues the number of the current iteration, the index response port receives a signal to start the next iteration, the index output issues the last iteration number when the loop ends.
  • Added variable ports - these are the ports of the variables created in the block configuration dialog. They provide for setting the initial values of variables, passing and receiving data from the loop body, as well as issuing data received at the last iteration of the loop. Initial values and response data can be checked by adding respective variables to the condition expression.

    With the added request and response ports, you can pass data for processing to the loop body, receive processed data, and then pass it for further processing in the next iteration, thereby implementing recurrent processing using the loop under the control of the While block. The added input and output ports allow you to set the initial data for processing and get the processing result at the end of the loop. For further details, see Recurrent processing.

  • Signal ports - these are two service ports of the same name: the @go input enables you to control the execution of this block, the @go output signals that the execution of this block is completed. A link to the @go input port ensures that the block is only executed when a value is received on that port. Linking to the @go output port allows you to prevent the execution of another block until this block has completed its operation.

  • Setting up condition requires composing a logical expression that evaluates to True or False depending on the current values of variables. In the condition expression, variable values are represented by the names of the respective variables.

All block ports and their settings are displayed in the Block properties pane when you select the block in the workflow diagram. In the block configuration dialog, you can view or change the condition expression, create variables, and set the initial values of variables, which is the same as assigning values to variable input ports.

Built-in ports

To start running the While block, all required value data must be passed to its input ports. The workflow can also control the startup of the While block by signaling over a link to the @go input port (see Signal ports).

On each iteration of the loop, the While block emits the values of the variables to their request ports. The block has a built-in index request port that can be used to pass the current iteration number to the loop body. The number of the first iteration is equal to the value on the index input port (0 by default); the number of each subsequent iteration is the number of the previous iteration incremented by one.

Data can also be passed to the loop body through request ports of added variables (see Added variable ports). If the request port of any added variable is connected to the loop body, you may not connect the index request port. The iteration counter operates within the While block regardless of whether that port is connected.

Having sent request data, the block waits for response data to appear on its response ports. The block has a built-in index response port that serves to signal the iteration completion from the loop body. To receive and check response data against the block condition, use response ports of added variables.

The index response port is normally connected if the block has no added variables. The response value received on this port is disregarded and cannot be checked against the block condition; it only signals that the execution of the loop body within this iteration is completed.

Having received the response values, the While block can check them against its condition. The exception is the index response port: instead of the received response value, the index variable is assigned the subsequent iteration number. If response values match the condition, the block issues values for the next iteration through its request ports. In particular, the index request port outputs the number of the next iteration. The iteration ends with the receipt of response data from the loop body through the response ports of the block variables.

At the end of the block execution, the index output port issues the last iteration number of the loop.

Implementing a for loop

You can use the While block to implement a loop with a given number of iterations - a for loop. Such a loop has a sequence of integers that define iterations, and a body that is executed at each iteration. A for loop is typically used when the number of iterations is known before entering the loop - such a loop is essentially a request to perform a certain sequence of actions a given number of times.

You can implement a for loop using the built-in index ports:

  1. Connect the index request port to the input of the loop body.
  2. Connect the output of the loop body to the index response port.
  3. In the condition expression, specify that the value of the index variable should be less than the desired number of iterations, such as "index"<100.

To pass the desired number of iterations to the block, you can create a variable, such as max_iter, and assign a value to that variable through its input port. The for loop condition in this case would be "index"⁠<⁠"max_iter".

Added variable ports

A basic function of the While block is to check responses against a given condition. The block can receive response values via the built-in index port, but the value thus obtained cannot be checked, as it is replaced by the subsequent iteration number. To check responses and other input data, variables are used that can be created in the While block configuration dialog.

Each variable adds four ports of the same name: an input port, a request port, a response port, and an output port. The names of all these ports are the same as the names of the variables that are listed in the Variables area of the block configuration dialog. To create a variable, click the button at the top of this area, and then enter a name for the new variable. This name identifies the variable in the condition expression and is also assigned to the ports of the variable.

Added ports enable you to change the value of the variable and return its current value to the workflow: the value of the input port is assigned to the variable at the beginning of the block execution, the request port passes the current value of the variable to the loop body, the value of the response port is assigned to the variable at the end of the iteration, and the output port returns the current value of the variable at the end of the loop. The variable retains its value between iterations of the loop, allowing data to be processed recurrently, since the value of the variable passed through the request port in the subsequent iteration is the value received through the response port in the previous iteration.

Recurrent processing

Variables and their ports enable recurrent data processing, where at each subsequent iteration, the results of the previous iteration are passed to the loop body. A typical example of such processing is a method of successive approximations that sets some initial value and calculates each new value based on the previous one. To enable such a recurrent sequence of calculations, the ports of the variable are used in the following manner:

  • Input port specifies the initial value of the recurrent sequence.
  • Request port feeds the loop body with the value received at the previous iteration.
  • Response port receives the value calculated at the current iteration.
  • Output port issues the value received at the final iteration.

On the first iteration of the loop, the variable's request port passes the value received though its input port to the loop body, then the block receives the response values. On subsequent iterations, the variable's request port passes the value that the variable's response port received in the previous iteration. After checking the condition, the next iteration is started, or the loop ends. The value received on the variable's response port in the final iteration is issued through its output port.

Signal ports

The While block has two ports intended to control the execution of the block - an input port named @go and an output port of the same name. The @go input port enables the workflow to start the block using a link to that port. The @go output port emits a value indicating that the block has completed execution.

With the @go input port, you can delay the execution of the block until a signal is sent to that port. The @go output port allows you to pass an execution trigger signal to the input of another block. Thus, the execution of one block can be made dependent on the completion of the execution of another block.

The @go input port receives a signal indicating that the block can start running. If this port is not connected anywhere, the block starts running as soon as it receives all the necessary data. However, if you connect a link to the @go input port, the block will wait for a signal on that port. Such a signal is any value received through a link from an output port of another block.

The signal to run the block can be any value passed via a link to the @go input port. This value only permits the execution and does not affect the operation of the block in any way. When the block execution completes, the @go output port emits a value that can be passed, for example, via a link with the @go input port of another block.

Setting up condition

Condition is a logical expression that can include variable values. In the condition expression, variable names in double quotes are used to represent variable values. The condition is deemed met if its expression evaluates to True.

To specify a condition, enter the condition expression in the Condition area of the block configuration dialog.

Condition expressions allow the use of basic comparison operators, such as "equals" ("a"=="b"), "less than" ("a"<"b"), "greater or equal" ("a">="b") and so on, as well as the and, or, and not Boolean operators. Arithmetic operators are also allowed. Parentheses allow you to control the order in which the expression is evaluated.

When composing expressions, observe the following basic rules:

  • Each variable name must be enclosed in double-quotes. For example, the expression "index"<100 checks the value of the variable named index. The variable name is enclosed in double quotes: "index".
  • If the variable value is an array, the element index must be outside the double quotes surrounding the name of the variable, for example: "result"[2]<0 (list item), "result"[3][0]>1 (matrix element).

    Array indexing is zero-based, so for example "result"[2] is the third element of the array represented by the value of the result variable.

  • If the variable value is a dictionary, the item key must be outside the double quotes, for example: "res"['error']<=0.1.

For more information on composing expressions, see Condition expressions.

Condition expressions

Conditions are defined by logical expressions that evaluate to True or False. A condition is deemed met if its expression evaluates to True. Condition expressions must conform to the basic Python syntax for expressions and may contain variables, literals, and operators.

The variable names in the condition expression are the names of the variables that are created in the block configuration dialog. When evaluating the expression, the name of the variable is replaced by its current value.

Strings and numbers can be used as literals. A literal string must be enclosed in single quotes ('string') - you cannot use double quotes because a double-quoted string is considered a variable identifier.

Compound values, such as lists, matrices, and dictionaries, can be passed to the expression through variables. The creation of such values within an expression is not allowed. For instance, a list literal [1, 2, 3] in an expression will cause an error when evaluating the expression.

The expression evaluates successfully when data of the expected type is substituted into it. If the current value of the variable does not match the expected data type, evaluation of the expression may fail. For instance, a numeric value of the variable "x" results in an error when evaluating the expression "x"[2]<0 because "x" is expected to be a list.

Operators

The following operators are allowed in condition expressions:

Comparison operators

  • == - equal to, not to be confused with the = assignment operator; when used with strings, the comparison is case-sensitive
  • < - less than
  • > - greater than
  • <= - less than or equal to
  • >= - greater than or equal to

Arithmetic operators

  • + - addition
  • - - subtraction
  • * - multiplication
  • / - division
  • % - division modulo (remainder of division)

Logical operators

  • and - True if both operands are True; otherwise, False
  • or - True if either operand is True; otherwise, False
  • not - True if the operand is False; False if the operand is True

Membership test operator

  • in - True if the left operand is contained in the right one; with strings, works as a substring search

Examples

Some condition expressions are:

  • "continue" and "index"<100 - check if the value of the continue variable is True and the value of the index variable is less than 100.
  • "x">=0 and "x"<=1 - check if the numeric value of the x variable is in the range from 0 to 1.
  • "r0"<"r1" and "r1"<"r2" and "r2"<"r3" - check if the numeric values of the variables r0, r1, r2, and r3 are strictly in ascending order.
  • "f_error"/"f"<0.05 - assuming numeric values of the f_error and f variables, check if the f_error to f ratio is less than 0.05.
  • "func"[0]<"func"[1] - assuming the value of the func variable is a list of numbers, check if the first number in the list is less than the second number (note that list indexing is zero-based).
  • "res"['error']>"threshold" - check if the value of the error key in the dictionary represented by the value of the res variable is greater than the value of the threshold variable.