# IF a THEN b ELSE c with arrays

## Contents

The construct, IF a THEN b ELSE c, generalizes appropriately if any or all of «a», «b», and «c» are arrays. In other words, it fully supports Intelligent Arrays. For example, if condition «a» is an array of Booleans (true or false values), it returns an array with the same index, containing «b» or «c» as appropriate:

`Variable X := -2 .. 2`

`If X > 0 THEN 'Positive' ELSE IF X < 0 THEN 'Negative' ELSE 'Zero' →`

X ▶ -2 -1 0 1 2 'Negative' 'Negative' 'Zero' 'Positive' 'Positive'

If «b» and/or «c» are arrays with the same index(es) as «a», it returns the corresponding the values from «b» or «c» according to whether «a» is true or false:

`IF X >= 0 THEN Sqrt(X) ELSE 'Imaginary' →`

X ▶ -2 -1 0 1 2 'Imaginary' 'Imaginary' 0 1 1.414

In this case, the expression Sqrt(X) is also indexed by **X**. The **IF **expression evaluates Sqrt(X) for each value of **X**, even the negative ones, which return `NAN`

, even though they are replaced by **Imaginary **in the result.

**To avoid evaluating all of «b» or «c»**: When IF a THEN b ELSE c is evaluated, the expression «a» is first evaluated completely. If its result is true, or if it results in an array where any value in the array is true, then the expression «b» is evaluated completely as an array operation, meaning the expression is evaluated for all values of all indexes contained within «b». Similarly, if «a» is false or «a» is an array and any element is false, the expression is «c» is evaluated in its entirety. Once both are computed, the appropriate values are picked out of these results according to the result of «a». Sometimes, you want to avoid evaluating elements of «b» or «c» corresponding to elements of «a» that give errors or Null results, to avoid wasting computation time on intermediate results that won’t be used in the final result, or because the computations cause evaluation errors, not just warnings. In such cases, you can use explicit iteration, using a For or While loop over index(es) of «a».

**Omitting ELSE**: If you omit the **ELSE c** part, it usually gives a warning when it is first evaluated.

If you click **Ignore Warnings***, *it returns Null for elements for which «a» is false:

`IF X >= 0 THEN Sqrt(X) →`

X ▶ -2 -1 0 1 2 «Null» «Null» 0 1 1.414

After you have clicked **Ignore Warnings**, it does not give the warning again, even after you save and reopen the model.

**ELSE c**part of an

**IF**construct only in a compound expression, when the

**IF a THEN b**is not the last expression, but rather is followed by ";". In this situation, the Null result is not part of the result of the compound expression, and it gives no warning, as shown in this example:

`BEGIN`

`VAR A[] := Min([X, Y]);`

`IF A < 0 THEN A := 0;`

`Sqrt(A)`

`END`

**Caveats of conditional side effects**: In the expression above, the empty brackets following **A **define **A **as array with no indexes (i.e., as atomic). Analytica will ensure that within the body of the expression where **A **is used, **A **will always be atomic, even if **X **or **Y **are array-valued. To do this, Analytica might need to iterate the expression. If you feel compelled to embed an assignment inside a **THEN **or **ELSE **clause, you should always make sure that the condition being tested is a *scalar *and not an array. In this case, because **A **has been declared to be 0-dimensional, the expression **A < 0 **is guaranteed to be scalar. If you cannot guaranteed that the **IF **clause will always be scalar, even if other indexes are added to your model in the future, then you should avoid using assignment from within a **THEN **or **ELSE **clause since Analytica evaluates **IF-THEN-ELSE** and an array operation. Without the brackets declaring A to be scalar, the **IF **clause would say “IF any value of A is less than zero THEN evaluate the assignment”, so the result would be an array of zeroes even if there is only a single negative number in **X **and **Y**. A better way to encode a conditional assignment, which properly array abstracts and has the intended effect, is as follows:

`BEGIN`

`VAR A := Min([X, Y] ;`

`A := IF A < 0 THEN 0 ELSE A;`

`Sqrt(A)`

`END`

**The dimensions of the result**: If «a» is an array containing some True and some False values, IF a THEN b ELSE c, evaluates both «b» and «c». The result contains the union of the indexes of all operands, «a», «b», and «c». But, if «a» is an atom or array whose value(s) are all true (1), it does not bother to evaluate «c» and returns an array with the indexes of «a» and «b». Similarly, if all atoms in «a» are false (0), it does not bother to evaluate «b» and returns an array with the indexes of «a» and «c». This means that the values in the condition «a» can affect whether «b» and/or «c» are evaluated, and which indexes are included in the result.

### IFALL a THEN b ELSE c

If you don’t want the dimensions of the result to vary with the value(s) in «a», use the **IFALL** construct. This is like the **IF** construct, except that it always evaluates «a», «b», and «c», and so the result always contains the union of the indexes of all of three operands.

**IFALL **requires the **ELSE c **clause. If omitted, it gives a syntax error.

### IFONLY a THEN b ELSE c

**IFALL **has the advantage over `IF`

(and **IFONLY**) that the dimensions of the result are always the same, no matter what the values of the condition «a». The downside is that if «a» is an array and all its atoms are True (or all are False), it wastes computational effort calculating «c» (or «b») even though its value is not needed for the result. **IFALL **also might waste memory (and therefore also time) by including the index(es) that are only in «c» (or «b») even though the result has the same values over those indexes. The standard **IF** construct might also waste some memory when all of the values of array «a» are True (or all are False), because the result includes any index(es) of «a» that are not indexes of «b» (or «c»), even though the result must be the same over such index(es).

In these situations you can use a third conditional construct, **IFONLY a THEN b ELSE c**. Like **IF**, when all atoms of «a» are True (or all False), it evaluates only «b» (or only «c»). But, unlike **IF**, the result in these cases does include any index(es) of «a» that are not indexes of «b» (or «c», respectively). Thus, **IFONLY **can be more memory-efficient.

### When to use IF, IFALL, or IFONLY

In most cases, you can just use **IF **without worrying about **IFALL **or **IFONLY**. The only reason to use **IFALL **is when you want to avoid the possibility that the dimensions of results can vary with values of inputs. The only reason to use **IFONLY **is when memory is tight and it’s common for condition «a» to be all true or all false.

If condition «a» is an atom or array containing only true (only False) values, **IF **and **IFONLY **evaluate only «b» (only «c»), whereas **IFALL **always evaluates both «b» and «c». The result of **IFONLY **contains the indexes of only «b» (only «c»). The result of **IF **contains the indexes of «a» and «b» (or «c»). The result of **IFALL **always contains the indexes of «a», «b», and «c», and so its dimensions do not depend on the values of «a».

If condition «a» is an array containing mixed true and false atoms, all three constructs behave identically: They evaluate «a», «b», and «c», and the result contains the union of the indexes of «a», «b», and «c».

**IFALL **requires the **ELSE **part. It is optional for **IF **and **IFONLY**, but strongly recommended except when it is one of multiple statements, and not the last, in a compound expression, enclosed in parentheses or **BEGIN ... END**.

## See Also

Enable comment auto-refresher