Session 10
Conditionals
Remarks on Conditions
String comparisons
So far, we've only dealt with simple numeric comparisons. But using the already established comparison operators (see while- and repeat-Loops) it's also possible to compare strings. In fact, we've done that already in the session on opening and saving files:
filename$ = chooseReadFile$: "Open a sound file"
if filename$ <> ""
Read from file: filename$
endif
Using the unequal-operator, this condition is true, if the content of filename$ is different from an empty string, i.e. if there is something stored in filename$ at all.
Application of the equal-operator is also straightforward:
if speaker$ = "male"
pitch_floor = 60
endif
This is true if the string male is stored in speaker$; if speaker$ contains Male or MALE or any other string, the condition is false. By the way, this example illustrates the context-sensitive meaning of "=": In the first occurrence (condition context, line 1) it's a comparison operator, in the second occurrence (assignment context, line 2) it's the assignment operator.
Other comparison operators (<, <=, >, and >=) are also applicable with strings, but they are far from being straightforward. Greater/less comparisons of strings in Praat are based on the ASCII sorting order, which could yield unexpected results. ASCII defines 95 standard printable characters in the following order:
!"#$%&'()*+,-./0123456789:;<=>?@
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
abcdefghijklmnopqrstuvwxyz{|}~
This means that numbers are less than upper case letters, which are less than lower case letters. Some examples:
Condition | Truth Value |
---|---|
"z" > "a" | true |
"Z" > "a" | false |
"a" < "b" | true |
"aaa" > "b" | false |
"aaa" > "a" | true |
"a" > "1" | true |
"a" < "999" | false |
To test this behavior try the following little script with different values assigned to s1$ and s2$:
s1$ = "A"
s2$ = "a"
clearinfo
if s1$ < s2$
appendInfoLine: s1$, " is less than ", s2$
elsif s1$ > s2$
appendInfoLine: s1$, " is greater than ", s2$
else
appendInfoLine: s1$, " is equal to ", s2$
endif
However, my advice is to try to avoid greater/less comparisons of strings, especially if the strings contain special characters like punctuation marks.
Combined conditions
It is possible (and often useful) to build complex conditions by combining simple conditions with the logical operators and or or. For instance, if you need to know if x is between 100 and 200 you can either implement two nested if-statements:
if x > 100
if x < 200
writeInfo: x, " is between 100 and 200"
endif
endif
or you combine the conditions with logical and:
if x > 100 and x < 200
writeInfo: x, " is between 100 and 200"
endif
The truth value of a complex and-condition is only true if both sub-conditions are true. Complex or-conditions, on the other hand, are true if at least one sub-condition is true. So, to ignore x-values between 100 and 200 you combine two conditions with or:
if x < 100 or x > 200
writeInfo: x, " is not between 100 and 200"
endif
Complex conditions may combine more than two simple conditions and involve more than one logical operator. You are allowed to use parentheses to control the order of evaluation of sub-conditions. To illustrate this, the following script detects x-values between 100 and 200 and between 300 and 400:
if (x > 100 and x < 200) or (x > 300 and x < 400)
writeInfo: x, " is between 100 and 200 or between 300 and 400"
endif
Any simple condition may be part of a complex condition:
if speaker$ = "male" and mean_pitch < 100
writeInfo: "deep male voice"
endif
Functions returning a truth value
Some built-in Praat functions return truth values and may serve as conditions, substituting comparisons. You already know one of these:
if fileReadable (gridname$)
exitScript: "The TextGrid was not saved (due to an existing file)!"
endif
fileReadable ()
tests whether the referred file is readable or not. If the file is readable, fileReadable ()
returns 1, which is equivalent to true, if it is not readable, the function returns 0, which is equivalent to false. It is permitted to combine functions like this with any other conditions using logical and or or.
There's a third logical operator which wasn't mentioned yet: not. It can be used to negate truth values. To ensure that a file is not readable before you do something, you can implement the following evaluation:
if not fileReadable (filename$)
# do something
endif
You can combine not with comparisons, but usually it's easier to just replace the comparison operator: not x = 10
is the same as x <> 10
.
Probably, fileReadable ()
is the only boolean function that is interesting for you—until you start writing scripts for the demo window: managing user input in the demo window depends heavily on boolean functions.
Undefined
An important job of conditionals is the evaluation of query results, because sometimes queries yield undefined or empty results. For example, if you want to use the minimum pitch of a sound to generate an intensity analysis you can do:
# remember selected sound
sound = selected ()
# pitch analysis
pitch = To Pitch: 0, 75, 600
# query minimum
minPitch = Get minimum: 0, 0, "Hertz", "Parabolic"
# remove pitch
Remove
# select sound
selectObject: sound
# intensity analysis
To Intensity: minPitch, 0, "yes"
That's fine as long as the selected sound has voiced parts. With an unvoiced sound, however, the query (3. command) is futile, which results in script abortion with an error message in the last line. What happens? Well, with unvoiced sounds there's no pitch and without pitch there's no minimum pitch, i.e. minimum pitch is undefined. In fact, the 'value' undefined is assigned to minPitch in this case. So, after variable substitution, the last line reads:
To Intensity: undefined, 0, "yes"
which, obviously, results in an error message. To avoid this, we should ensure that minPitch is not undefined before using it in a statement:
sound = selected ()
pitch = To Pitch: 0, 75, 600
minPitch = Get minimum: 0, 0, "Hertz", "Parabolic"
Remove
selectObject: sound
# ensure that minPitch is kosher
if minPitch <> undefined
To Intensity: minPitch, 0, "yes"
else
# react to undefined variable, e.g.:
writeInfo: "minPitch is undefined"
endif
Only numerical variables can get undefined, and only in query contexts (to be accurate: you can assign undefined to a numerical variable yourself; but why would you do that?). And don't confuse undefined with 0! Queries may return 0 as a result (e.g.
String variables, on the other hand, can't be undefined, they can only be empty. If you query labels in a TextGrid for instance, it's possible to come across empty intervalls. Taking this into account is nothing new: Just compare the query result with the empty string. An example from the loop session:
clearinfo
numberOfTiers = Get number of tiers
for i to numberOfTiers
numberOfIntervals = Get number of intervals: i
for j to numberOfIntervals
label$ = Get label of interval: i, j
# react to empty intervalls
if label$ = ""
appendInfoLine: "no label found"
else
appendInfoLine: label$
endif
endfor
endfor
Recap
Conditionals start with the keyword if
, followed by a condition in the same line, and end with endif
. One optional else
section and any number of optional elsif
sections are allowed. An associated code block follows the if
- and the optional elsif
- and else
-statements.
Conditionals allow the implementation of processing alternatives (‘branching'). Which alternative is executed depends on the evaluation of conditions. No more than one code block within an if-statement is executed, namely the one following the first true condition or the one following else
if an else
section is available and all conditions are false. With a simple if-statement (only one condition, no else
section) not even one code block may be executed—if the condition is false.
Most conditions are formulated as comparisons. Praat provides 6 comparison operators to compare numeric or string entities (literal values, variables, and formulas). Conditions can be combined using the logical operators and or or.