Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
calculate_functors [2026/07/29 13:34] hermann |
calculate_functors [2026/08/21 01:03] (current) hermann [1. General Operators] |
||
|---|---|---|---|
| Line 194: | Line 194: | ||
| ^ Precedence ^ Operator ^ Syntax ^ Description ^ Example ^ | ^ Precedence ^ Operator ^ Syntax ^ Description ^ Example ^ | ||
| | 1 | **Conditional** | ''if EXPR then EXPR else EXPR'' | Evaluates to the second expression if the condition is true, otherwise the third. Can be nested. | ''if i1 > 0 then i1 else 0'' | | | 1 | **Conditional** | ''if EXPR then EXPR else EXPR'' | Evaluates to the second expression if the condition is true, otherwise the third. Can be nested. | ''if i1 > 0 then i1 else 0'' | | ||
| - | | 2 | **Boolean Or** | ''EXPR or EXPR'' / ''EXPR || EXPR'' | True if either argument is true. **Short-circuit evaluated** — the right side is skipped if the left is already true. | ''isNull(i1) || i1 > 100'' | | + | | 2 | **Boolean Or** | ''EXPR or EXPR'' / ''EXPR || EXPR'' | True if either argument is true. **Short-circuit evaluated** — the right side is skipped if the left is already true. | ''isNull(i1) || i1 > 100'' | |
| | 2 | **Boolean And** | ''EXPR and EXPR'' / ''EXPR && EXPR'' | True only if both arguments are true. **Short-circuit evaluated** — the right side is skipped if the left is already false. | ''not isNull(i1) and i1 > 0'' | | | 2 | **Boolean And** | ''EXPR and EXPR'' / ''EXPR && EXPR'' | True only if both arguments are true. **Short-circuit evaluated** — the right side is skipped if the left is already false. | ''not isNull(i1) and i1 > 0'' | | ||
| | 3 | **Equal** | ''EXPR = EXPR'' / ''EXPR == EXPR'' | True if both sides are equal. | ''i1 == 2'' | | | 3 | **Equal** | ''EXPR = EXPR'' / ''EXPR == EXPR'' | True if both sides are equal. | ''i1 == 2'' | | ||
| Line 529: | Line 529: | ||
| If the computed real value exceeds the range of the chosen Cell Type, that cell is also written as null rather than wrapping or clipping. This range check applies to [[Calculate Map]] and [[Calculate Categorical Map]] only — the only two functors that accept a Cell Type parameter and write to a typed cell grid. | If the computed real value exceeds the range of the chosen Cell Type, that cell is also written as null rather than wrapping or clipping. This range check applies to [[Calculate Map]] and [[Calculate Categorical Map]] only — the only two functors that accept a Cell Type parameter and write to a typed cell grid. | ||
| - | ===== Guarding a conditional's test against null ===== | + | ===== When a conditional's test needs guarding against null ===== |
| - | A conditional whose test doesn't explicitly check for null still behaves correctly as long as the same operand being tested is also what gets returned. ''if i1 = 2 then 10 else i1'' is internally equivalent to ''if isNull(i1) then null else if i1 = 2 then 10 else i1'' — when ''i1'' is null, the whole expression is null, which is exactly the desired behaviour when reclassifying a single map in place. | + | A conditional's test poisoning the whole result is only a problem when the tested operand's nullness shouldn't determine whether the entire expression is invalid. Reclassifying ''i1'' in place — ''if i1 = 2 then 10 else i1'' — needs no explicit guard, since a null ''i1'' making the reclassified result null for that cell is exactly the desired behaviour: the expression is internally equivalent to ''if isNull(i1) then null else if i1 = 2 then 10 else i1''. |
| - | The same pattern breaks when the null being tested belongs to a //different// operand than the one being preserved. Combining a landscape map (''i1'') with a road map (''i2'', where 1 marks a road and null marks everything else) might suggest ''if i2 = 1 then 1 else i1'' — but this is internally equivalent to ''if isNull(i2) then null else if i2 = 1 then 1 else i1''. Since most cells are null in a sparse road map, most of the output becomes null too, discarding ''i1'''s landscape values everywhere a road isn't present. | + | The same mechanism causes a real bug when the operand being tested isn't the one the null-ness should actually be judged against. Combining a landscape map (''i1'') with a road map (''i2'', where 1 marks a road and null marks everything else) might suggest ''if i2 = 1 then 1 else i1'' — but this is internally equivalent to ''if isNull(i2) then null else if i2 = 1 then 1 else i1''. Since most cells are null in a sparse road map, most of the output becomes null too, discarding ''i1'''s landscape values everywhere a road isn't present — even though ''i2'' being null says nothing about whether ''i1'''s value is valid. |
| Guarding the test with ''isNull'' fixes it: ''if not isNull(i2) and i2 = 1 then 1 else i1'' only tests ''i2'' once it's confirmed non-null, so a null road cell falls through to the landscape value in ''i1'' instead of nulling the result. | Guarding the test with ''isNull'' fixes it: ''if not isNull(i2) and i2 = 1 then 1 else i1'' only tests ''i2'' once it's confirmed non-null, so a null road cell falls through to the landscape value in ''i1'' instead of nulling the result. | ||