Control Structures: Difference between revisions
imported>PhilmASTErpLus →repeat until: Tried it myself, needs a semicolon at the end |
→catch: fix notes |
||
(12 intermediate revisions by 6 users not shown) | |||
Line 74: | Line 74: | ||
</syntaxhighlight>}} | </syntaxhighlight>}} | ||
{{ | |||
CodeSample| | |||
description="variable" and every "value" can be expressions as complex as wanted, as long as their end result is always of the same datatype. NO silent conversion is done, so the following '''will generate an error''':| | |||
code= | |||
<syntaxhighlight> | |||
switch ( 10 + 1 ) | |||
{ | |||
case "1" + "1": | |||
// Whatever you want; it doesn't matter, as 11 can't be compared with "11" | |||
break; | |||
} | |||
</syntaxhighlight>}} | |||
{{ | {{ | ||
CodeSample| | CodeSample| | ||
description=If switch has no parameter, | description=Putting a ''break'' in each case isn't mandatory. If no ''break'' is placed on the path taken, once the last line of the case is reached, execution simply goes on, and will execute (will "fall-through") the content of the next case/default.| | ||
code= | |||
<syntaxhighlight> | |||
switch ( int guess_my_age ) | |||
{ | |||
case 24: | |||
case 26: | |||
print("You were SO close!"); | |||
break; | |||
case 25: | |||
print("You got it!"); | |||
break; | |||
default: | |||
print("Nope"); | |||
} | |||
</syntaxhighlight>}} | |||
{{ | |||
CodeSample| | |||
description=The default value of a switch structure's parameter is true. This means that if switch has no parameter, it is the same as submitting '''switch (true) [...]'''. Each case is then evaluated as a boolean expression, like a string of if-then statements.| | |||
code= | code= | ||
<syntaxhighlight> | <syntaxhighlight> | ||
Line 93: | Line 124: | ||
} | } | ||
</syntaxhighlight>}} | </syntaxhighlight>}} | ||
The default doesn't need to be the last statement (which is useful when using the aforementioned fall-through mechanic). If the default is placed early (e.g. as the first statement), it will NOT have priority over the other cases; if the value submitted to the switch matches one of the cases, execution will jump to it, even if placed after the default. <br> | |||
Putting a ''break'' at the end of the switch is generally considered good practice, but achieves nothing. | |||
==Unconditional== | |||
===try / finally=== | |||
{{ | |||
CodeSample| | |||
description=Block1 is executed as normal, and then block2 is executed regardless of whether | |||
block1 finished normally, generated an error, or encountered a 'return', | |||
'break', or 'continue' statement.| | |||
code= | |||
<syntaxhighlight> | |||
try | |||
{ | |||
// block1 | |||
} | |||
finally | |||
{ | |||
// block2 | |||
} | |||
</syntaxhighlight>}} | |||
===static=== | |||
{{ | |||
CodeSample| | |||
code= | |||
<syntaxhighlight> | |||
static | |||
{ | |||
// any statements here | |||
// are only going to be executed once at the | |||
// first time the script executes in a session. | |||
} | |||
</syntaxhighlight>}} | |||
{{ | |||
CodeSample| | |||
description=Single-statement static declarations may omit the curly braces.| | |||
code= | |||
<syntaxhighlight> | |||
static | |||
// curly braces aren't required if only one statement follows the static keyword. | |||
</syntaxhighlight>}} | |||
Keep in mind that when using imports, a static call will only apply to the script that imports it. | |||
*If foo.ash has a static variable, but bar.ash imports foo.ash, KoLmafia will keep separate values for both foo.ash and bar.ash. | |||
*If bar.ash is changed, its static variable is reset, but not foo.ash's. | |||
*If foo.ash is changed, both scripts' variables are reset. | |||
===catch=== | |||
The '''<code>catch</code> statement''' can be used to suppress any errors that occur while evaluating an expression or a code block: | |||
<i style="color: grey">// Suppress any errors that occur while evaluating the expression.</i> | |||
'''catch''' ''expression''; | |||
<i style="color: grey">// Suppress any errors that occur while evaluating the code inside the block.</i> | |||
'''catch''' { | |||
''statements''; | |||
} | |||
The '''<code>catch</code> expression''' can be used to capture the error message, if an error occurs while evaluating an expression or a code block: | |||
<i style="color: grey">// If an error occurs while evaluating the expression, | |||
// store the error message in a string variable named 'error_msg'. | |||
// If there is no error, store an empty string ("").</i> | |||
string error_msg = '''catch''' ''expression''; | |||
<i style="color: grey">// If an error occurs while evaluating the code inside the block, | |||
// store the error message in a string variable named 'error_msg'. | |||
// If there is no error, store an empty string ("").</i> | |||
string error_msg = '''catch''' { | |||
''statements''; | |||
}; | |||
Notes: | |||
* Unlike other programming languages (e.g. Java, JavaScript), <code>catch</code> cannot be used to catch an error that occurs in a preceding <code>try</code> statement. | |||
* <code>catch</code> cannot be used to "escape" an {{f|abort}} call--KoLmafia will always halt the script. | |||
The current form was finalized in r20479. See [https://kolmafia.us/threads/construct-to-swallow-errors-in-ash.25486/ discussion thread]. | |||
==Loops== | ==Loops== | ||
Line 144: | Line 251: | ||
If you don't specify "c", it defaults to incrementing/decrementing by 1. The first iteration is at a and the last is at b (that is to say, it goes from a to b, inclusive). | If you don't specify "c", it defaults to incrementing/decrementing by 1. The first iteration is at a and the last is at b (that is to say, it goes from a to b, inclusive). | ||
a, b and c are automatically converted to integers. You can't use floats here. | |||
===foreach=== | ===foreach=== | ||
Line 220: | Line 329: | ||
===continue=== | ===continue=== | ||
Continues on to the next iteration of the loop (skipping any statements in this iteration that occur after the continue statement). In a switch statement, continue is allowed | Continues on to the next iteration of the loop (skipping any statements in this iteration that occur after the continue statement). In a switch statement, continue is allowed if the switch is inside a loop, and acts as any other continue. | ||
===return=== | ===return=== | ||
Exits the function and returns the value following the return statement, if specified. Note that the value's datatype must match that of the function itself (void functions can only use return by itself). | Exits the function and returns the value following the return statement, if specified. Note that the value's datatype must match that of the function itself (void functions can only use return by itself). | ||
If in a switch statement, don't place a break after a return! | |||
===exit=== | |||
Exits the script. Using return when in main() achieves the same effect. Note that while this will end the current script, it will not stop automation. | |||
[[Category: | [[Category:Scripting]] |
Latest revision as of 13:11, 6 March 2021
Conditional
if
if ( boolean )
{
// any statements here
// are only going to be executed
// if the boolean returns true
}
Single-statement conditionals may omit the curly braces.
if ( boolean )
// curly braces aren't required if only one statement follows the conditional
else
if ( boolean )
{
// statements if true
}
else
{
// the statements here
// are only going to be executed
// if the boolean returns false
}
else if
if ( boolean 1 )
{
// statements if true
}
else if ( boolean 2 )
{
// the statements here
// are only going to be executed
// if boolean 1 returns false
// & boolean 2 returns true
}
switch
switch ( variable )
{
case value:
// statements if variable == value
// more such statements
break;
//repeat above for as many values as desired
default:
// statements executed if
// none of the cases were true
}
"variable" and every "value" can be expressions as complex as wanted, as long as their end result is always of the same datatype. NO silent conversion is done, so the following will generate an error:
switch ( 10 + 1 )
{
case "1" + "1":
// Whatever you want; it doesn't matter, as 11 can't be compared with "11"
break;
}
Putting a break in each case isn't mandatory. If no break is placed on the path taken, once the last line of the case is reached, execution simply goes on, and will execute (will "fall-through") the content of the next case/default.
switch ( int guess_my_age )
{
case 24:
case 26:
print("You were SO close!");
break;
case 25:
print("You got it!");
break;
default:
print("Nope");
}
The default value of a switch structure's parameter is true. This means that if switch has no parameter, it is the same as submitting switch (true) [...]. Each case is then evaluated as a boolean expression, like a string of if-then statements.
switch
{
case boolean expression:
// if the expression is true, this statement is executed
// more such statements
break;
//repeat above for as many conditionals as desired
default:
// statements executed if
// none of the cases were true
}
The default doesn't need to be the last statement (which is useful when using the aforementioned fall-through mechanic). If the default is placed early (e.g. as the first statement), it will NOT have priority over the other cases; if the value submitted to the switch matches one of the cases, execution will jump to it, even if placed after the default.
Putting a break at the end of the switch is generally considered good practice, but achieves nothing.
Unconditional
try / finally
Block1 is executed as normal, and then block2 is executed regardless of whether block1 finished normally, generated an error, or encountered a 'return', 'break', or 'continue' statement.
try
{
// block1
}
finally
{
// block2
}
static
static
{
// any statements here
// are only going to be executed once at the
// first time the script executes in a session.
}
Single-statement static declarations may omit the curly braces.
static
// curly braces aren't required if only one statement follows the static keyword.
Keep in mind that when using imports, a static call will only apply to the script that imports it.
- If foo.ash has a static variable, but bar.ash imports foo.ash, KoLmafia will keep separate values for both foo.ash and bar.ash.
- If bar.ash is changed, its static variable is reset, but not foo.ash's.
- If foo.ash is changed, both scripts' variables are reset.
catch
The catch
statement can be used to suppress any errors that occur while evaluating an expression or a code block:
// Suppress any errors that occur while evaluating the expression. catch expression; // Suppress any errors that occur while evaluating the code inside the block. catch { statements; }
The catch
expression can be used to capture the error message, if an error occurs while evaluating an expression or a code block:
// If an error occurs while evaluating the expression, // store the error message in a string variable named 'error_msg'. // If there is no error, store an empty string (""). string error_msg = catch expression; // If an error occurs while evaluating the code inside the block, // store the error message in a string variable named 'error_msg'. // If there is no error, store an empty string (""). string error_msg = catch { statements; };
Notes:
- Unlike other programming languages (e.g. Java, JavaScript),
catch
cannot be used to catch an error that occurs in a precedingtry
statement. catch
cannot be used to "escape" anabort()
call--KoLmafia will always halt the script.
The current form was finalized in r20479. See discussion thread.
Loops
while
while ( boolean )
{
// as with an if statement
// this area is only entered
// if the boolean tests true
// once all this is done
// it goes back to the begining
// and will keep executing
// as long as the boolean remains true
}
repeat until
This is similar to the do...while loop, but only ends the loop when the boolean
expression evaluates to true
.
repeat
{
// this is the same
// as the while loop above
// with one exception:
// all of this code will
// execute at least once
// as the test doesn't occur
// until the very end
} until ( boolean );
for
for x from a to b by c {
//do stuff
}
Above is the general case. You don't need to specify whether it's going up or down - although doing so by using upto or downto does allow a runtime check to make sure you didn't screw up.
If you don't specify "c", it defaults to incrementing/decrementing by 1. The first iteration is at a and the last is at b (that is to say, it goes from a to b, inclusive).
a, b and c are automatically converted to integers. You can't use floats here.
foreach
foreach key in aggregate {
//do stuff
}
Assigns each key in the supplied map or slice to "key
" and iterates through the map. Due to how maps are handled, foreach
is guaranteed to iterate through the map in sorted order.
For example:
boolean [int][string] map;
map[15]["test"] = true;
foreach int_index in map {
print(int_index); //this will print '15' once, since there is only one valid value for this index
foreach string_index in map[int_index] //this iterates over the "slice" of the map where 1 is fixed as the index
{
print(string_index); //This will print "test" once, since there is only one valid value for this index
print(map[int_index][string_index]); //this will print "true"
}
}
So the output is
15 test true
For a multidimensional map, instead of nesting foreach
statements two iterators can be used inline.
foreach x, y in map {
//do stuff
}
This is identical to:
foreach x in map {
foreach y in map[x] {
//do stuff
}
}
You can also directly specify the value stored in the map by specifying one more variable than the number of keys in the map:
//string [string, int, item] my_map;
foreach s, i, m, value in my_map {
print( s + ", " + i + ", " + m + " => " + value );
}
See the page for Data Structures for more information on aggregates.
Continuation & Exiting
Like many languages with looping structures, ASH supports the break and continue statements. All looping structures (for, while, repeat until, and foreach) support these statements.
break
Breaks out of the smallest enclosing loop. In a switch statement, breaks out of the switch statement. Execution resumes at the first statement after the end of the loop/switch statement.
continue
Continues on to the next iteration of the loop (skipping any statements in this iteration that occur after the continue statement). In a switch statement, continue is allowed if the switch is inside a loop, and acts as any other continue.
return
Exits the function and returns the value following the return statement, if specified. Note that the value's datatype must match that of the function itself (void functions can only use return by itself). If in a switch statement, don't place a break after a return!
exit
Exits the script. Using return when in main() achieves the same effect. Note that while this will end the current script, it will not stop automation.