This chapter covers how to build expressions for evaluating values and combining those results logically in a conditional expression (typically used for the control blocks described in the next chapter):
- Various comparison operators and methods used to evaluate values
- The boolean operators for logically combining multiple evaluations of values
Acorn offers many operators and methods for evaluating values against specific criteria:
- Equivalence operators: are two values the same?
- Comparison operators: is one value greater than another?
- Pattern matching operators: does a value match a specific pattern?
- Evaluation methods: does a value comply with some type-specific criteria?
Note: Evaluation operators have lower precedence than arithmetic operators, but higher than assignment.
The '==' operator returns true if two values have the same content and type. '!=' (not equal) is true if they do not. For example:
x == 3.0 # Returns true if the local variable x's value is 3.0 x+1 != y # Returns true if x is not exactly 1 greater than y 2 == 2.0 # Returns false, because the Integer vs. Float types do not match true != false # Returns true
The equality check is not always exact or rapid, depending on the value's type. Since floating point calculations are imprecise and can introduce "rounding errors", two Float values will be declared "equal" if they are sufficiently close. Large, complex value types, such as lists, can take time, as it requires comparing every element in the list for equivalence.
The '===' operator offers a rapid way to determine if two values are literally the same value. Two floats would have to match exactly, with no margin for calculation error. Two List or Text values would have to point to the same List or Text value, as a copy made of a list would not be exactly equal using the '===' operator.
Comparison operators determine whether one value is greater or less than some other value. They only work well on value types that are comparable (such as Integer, Float and Text).
x >= 2 # Returns true if x is greater than or equal to 2 (>, <, <= are variants) 2 <= 2.0 # Returns false any time the types do not agree
Float values are compared using the same closeness criteria described above. For one Float value to be less than another, it must be less by at least the comparable closeness.
All comparison operators use the '<=>' method to perform the comparison. It returns an Integer (rather than true or false): -1 if less, 0 if equal, and 1 if greater. It returns 'null' if two values are not comparable. Any Type that implements this method becomes comparable:
# Make frosty comparable to other values frosty # Comparison returns 1, as frosty is always the greatest! '<=>': [x] 1 frosty > Float.Infinity # true
The rocket-ship operator can be used directly:
-2 <=> 4 # Returns -1
Pattern Matching Operator
The '~~' pattern match operator determines whether a specific value matches a particular pattern. A pattern is any value that can responds to the '~~' method with a non-null value. Thus, all sorts of pattern-matchers can be built, such as set membership, types used, regular expression, search selectors (e.g., for parts), parsers, input decoders, translaters, information analysis, etc.
These examples use a Range as a pattern.
5 ~~ 1 .. 7 # Returns true, since 5 is within the range between 1 and 7
Some important notes about pattern matching:
- The match operator '~~' is the only ones that applies the implied method ('~~') to the right hand value (the pattern) rather than the left. Thus, a~~b is implemented as: b.'~~'(a). This exception makes the code easier to read and mirrors the order on the 'match' statement, while still putting the matching work on the pattern, where it belongs.
- A pattern match always returns false on a failed match. A successful match may return true, or (more interestingly) it may return a value calculated as a result of the match. For example, matching text to a regular expression might return a list containing extracted text segments that matched certain numbered slots.
- When a type does not implement the '~~' method, the Any Type ensures that the '==' method will be used in its place. Thus two equal values will always match.
Beyond the evaluation operators listed above, there are many type-specific methods which can be used to evaluate values. This is particularly true for any method whose name ends with a question-mark (indicating it returns a true or false value). For example:
list.empty? # Returns true if list has no members, false otherwise
A conditional expression evaluates to some value whose "truth" is used to determine the sequence of statement execution for a control block or clause. Such expressions are considered false if its evaluated value is either false or null. Any other value of any type, including true, 0 and "" will be considered true.
A conditional expression can be as simple as a single literal or variable value. More commonly, it involves the use of one of the evaluation operators or methods described above. The boolean operators described below can be used to logically transform multiple evaluations into a single value that is considered true or false for control flow purposes.
Acorn has three boolean operators: and ('&&'), or ('||') and not ('!'). One can use either the word or the symbol. For example:
0==3 or not 2<3 and 3==3 # Returns true ('and' is evaluated before 'or') 0==3 || !2<3 && 3==3 # equivalent to above
The logical operators work mostly how you would expect:
- As mentioned earlier, they treat null and false as false. All other values are considered true.
- The boolean operators && and || will stop evaluating any time the answer is known for sure.
Thus, the .empty? method will never be performed in this expression (making it similar to a simple 'if' statement):
false and list.empty?
- Although ! only returns a value of either true or false, || and && simply returns the
last value checked (which could be a non-Bool value). For example:
val1 = null val2 = 4.5 val = val1 || val2 # val is now 4.5
- The logical operators have a lower precedence than evaluation operators, but higher than assignment. ! has the highest precedence, then &&, then ||.
Note: In case it is not obvious by now, these boolean operators are not the same as the boolean bit-arithmetic operators found in other languages. There should not be much need for them in Acorn, but they are provided as Integer methods (.not, .and, .or, .xor, .shl, .shr) if they are needed.
Note 2: If a comparison operator or boolean operator is used outside the context of a control structure that wants a conditional expression (e.g., if), in most cases it will evaluate to a value of 'true' or 'false'. If the conditional expression just uses 'and' or 'or' operators, its value will be the last value evaluated in that expression.