If-Then-Else
Release: |
4.6 • 5.0 • 5.1 • 5.2 • 5.3 • 5.4 • 6.0 • 6.1 • 6.2 • 6.3 • 6.4 • 6.5 |
---|
If a Then b Else c
This conditional expression evaluates and returns «b» if «a» is true, or «c» if «a» is false.
Variable X := 1M
Variable Y := 1
If X > Y Then X Else Y → 1M
Omitting Else
It is possible to omit the Else clause:
If X < Y Then MsgBox("Unexpected: X was found to be less than Y");
X - Y
When the Else clause is omitted and «a» is false, a warning will be issued if the result value is actually used (in the above example, it isn't used, so no warning results). If you ignore the warning, the result is Null.
Usually, you should omit the Else «c» part only in a compound expression, where Then «b» is followed by a semi-colon, followed by other expressions. In such case, the result of If-Then is not actually used. See also #Perils of Side-Effects below.
Array Parameters
Conditional expressions get more interesting when they work on arrays, in which case array-abstraction generalizes to multi-dimensional cases. When «a» is an array with a mixture of true and false values, the result is an array with the same indexes as «a», which individual cells containing «b» or «c» as appropriate.
Variable x := -2..2
x ▶ -2 -1 0 1 2 'Negative' 'Negative' 'Zero' 'Positive' 'Positive'
If «b» or «c» are arrays with the same indexes as «a», the corresponding values from «b» or «c» are returned according to whether «a» is true or false.
x ▶ -2 -1 0 1 2 'Imaginary' 'Imaginary' 0 1 1.414
When «b» and «c» are evaluated
If-Then-Else processes all its parameters using array operations. The full arrays for «b» is computed whenever «a» contains at least one true value. So in the earlier example:
the square root of x is computed even for the negative values of X, even though they are replaced with 'Imagingary in the final result. Similarly, if «a» contains at least one false value, then «c» is computed entirely, even for values where «a» is true.
The apparently wasted computation is usually more than offset by the faster computation obtained from Analytica's ability to process the expressions using array processing. However, this not always true, and in some cases a case failing the antecedent condition can lead to an error when «b» (or «c») is evaluated.
To avoid evaluating «b» or «c»
The entire evaluation of «b» is skipped (i.e., the expression for «b» is not evaluated at all) when «a» is scalar false, or is an array containing only false values. Likewise, «c» is not evaluated when «a» is scalar true or when «a» is an array containing only true values.
To avoid computations for sub-arrays of «b» (or of «c»), you must employ explicit iteration (via User-Defined Functions using Parameter Dimensionality Qualifiers. Usually the iteration is written in such a way to ensure that «a» is a scalar each time the conditional is processed.
The overhead of explicit iteration may slow down computation substantially. As long as you aren't experiencing errors, in the vast majority of cases it is better to simply let «b» and «c» be evaluated in full.
Embedded Warnings
When Sqrt(x) is evaluated on a negative number, an embedded warning is issued. When that warning occurs from within an expression embedded in «b» or «c», it might have no effect on the final result if «a» doesn't select it. When the embedded warning occurs, Analytica figures out whether the warning could potentially impact the final result, or whether it will end up being ignored. When Analytica concludes that the case generating the warning does not impact the final result, then the warning is not shown to the the user, even if Show Result Warnings is on. This logic makes it possible for you to write very intuitive expressions to avoid problematic cases, as was done with the Sqrt example above; however, the logic to determine whether the warning will impact the final result does consume computational cycles. If you determine a particular computation involving If-then-else, where embedded warnings are probably occurring within «b» or «c», a speed-up can be obtained by surrounding the expression with a call to IgnoreWarnings():
IgnoreWarnings(If x >= 0 Then Sqrt(x) else 'Imaginary')
In this example, no change to behavior occurs, but speed-up occurs because the extra process to determine whether the case generating the warning is eventually ignored is skipped.
Perils of Side-Effects
Extreme care should be exercised when using Assignment Operators within conditionals. When dealing with array cases, assignment occuring within «b» or «c» is likely to produce counter-intuitive results.
The following example illustrates the point:
When x and y are scalar values, the above expressions produces the intuitive result. However, consider what happens when x and y are arrays:
i → 1 2 3 4 x → 3 -4 2 1 y → 2 5 7 -1
After the initial assignment, a becomes:
i → 1 2 3 4 a → 2 -4 2 -1 a < 0 → 0 1 0 1
But now, when the conditional is evaluated, a<0 is an array containing at least one true value, so the Then clause is evaluated. The assignment a:=0 sets the local variable a to scalar 0. So the final result if evaluating the expression is scalar 0 -- not an array indexed by i.
A good rule of thumb is that if you cannot guarantee that the If condition, «a», is scalar, do not embed assignment operations inside the Then or Else parts.
To guarantee that a is scalar, the expression could be re-expressed as:
This expression causes looping when x or y is array-valued. To continue leveraging the efficiency of array processing, it may be better to avoid looping. In most cases, you can avoid placing assignment inside «b» or «c» in a manner such as:
Dimensionality of the Result
When If «a» Then «b» Else «c»
is evaluated, the result will have the union of the dimensions of the parameters that get evaluated. So, the result will also include any dimensions of «a», along with any dimensions of «b» if «a» was true anywhere, along with any dimensions of «c» if «a» was false anywhere.
Because of this conditional nature, it is not possible to predict the dimensionality of the result in advance before you know the result of «a». Dimensions may or may not be included depending on whether «b» or «c» are actually evaluated.
Ifall-Then-Else
When you want to ensure that the final dimensionality is always the same, regardless if the final values for «a», you can employ the Ifall «a» Then «b» Else «c»
variation. Ifall always evaluates all parameters, «a», «b» and «c», and includes the union of all their dimensions in the final result. Thus, the final dimensionality is independent of whether the individual elements of «a» are true or false.
When you use Ifall-Then-Else
, you incur two hits to efficiency. First, since «b» and «c» are always evaluated in every case, evaluations occur that would be skipped by If-Then-Else
when «a» is constant. Second, because the result may have more dimensions, downstream computations may have to compute using larger arrays.
Ifonly-Then-Else
In contrast to the Ifall-Then-Else
construct, Ifonly-Then-Else
may actually return a result with fewer dimensions than If-Then-Else
. Smaller arrays can reduce the total number of computations performed by downstream operations, and thus improve efficiency.
The distinction between If and Ifonly occurs when «a» is contains all true. In this case, as with If, «b» will be evaluated and «c» will not be evaluated. The difference is that If includes the dimensions of «a» in the result while Ifonly does not. When «a» is a constant array, we can think of this as being equivalent to a scalar in the eyes of array abstraction, so Ifonly reduces the antecedent to a scalar prior to evaluating the remainder of Ifonly-Then-Else
.
Dimensionality Summary Table
Case Dimensionality of result for If «a» Then «b» Else «c»
Ifonly «a» Then «b» Else «c»
Ifall «a» Then «b» Else «c»
«a» is everywhere true D^{a} U D^{b} D^{b} D^{a} U D^{b} U D^{c} «a» is everywhere false D^{a} U D^{c} D^{c} D^{a} U D^{b} U D^{c} «a» contains both true and false D^{a} U D^{b} U D^{c} D^{a} U D^{b} U D^{c} D^{a} U D^{b} U D^{c}
Enable comment auto-refresher