* Switch out l() syntax to [] in docs * Using map syntactic sugar as well. Co-authored-by: gnembon <41132274+gnembon@users.noreply.github.com>
6.7 KiB
Loops, and higher order functions
Efficient use of these functions can greatly simplify your programs and speed them up, as these functions will
internalize most of the operations that need to be applied on multiple values at the same time. Most of them take
a list argument which can be any iterable structure in scarpet, including generators, like rect, or range,
and maps, where the iterator returns all the map keys
Loops
break(), break(expr), continue(), continue(expr)
These allow to control execution of a loop either skipping current iteration code, using continue, or finishing the
current loop, using break. break and continue can only be used inside for, c_for, while, loop, map,
filter, reduce as well as Minecraft API block loops, scan and volume
functions, while break can be used in first as well. Outside of the internal expressions of these functions,
calling break or continue will cause an error. In case of the nested loops, and more complex setups, use
custom try and throw setup.
Please check corresponding loop function description what continue and break do in their contexts, but in
general case, passed values to break and continue will be used in place of the return value of the internal
iteration expression.
c_for(init, condition, increment, body)
c_for Mimics c-style tri-arg (plus body) for loops. Return value of c_for is number of iterations performed in the
loop. Unlike other loops, the body is not provided with pre-initialized _ style variables - all initialization
and increments has to be handled by the programmers themselves.
break and continue statements are handled within body expression only, and not in condition or increment.
c_for(x=0, x<10, x+=1,
c_for(y=0, y<10, y+=1,
print(str('%d * %d = %d', x, y, x*y))
)
)
for(list,expr(_,_i))
Evaluates expression over list of items from the list. Supplies _(value) and _i(iteration number) to the expr.
Returns the number of times expr was successful. Uses continue and break argument in place of the returned
value from the expr(if supplied), to determine if the iteration was successful.
check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) ); for(range(1000000,1100000),check_prime(_)) => 7216
From which we can learn that there is 7216 primes between 1M and 1.1M
while(cond, limit, expr)
Evaluates expression expr repeatedly until condition cond becomes false, but not more than limit times.
Returns the result of the last expr evaluation, or null if nothing was successful. Both expr and cond will
recveived a bound variable _ indicating current iteration, so its a number.
while(a<100,10,a=_*_) => 81 // loop exhausted via limit while(a<100,20,a=_*_) => 100 // loop stopped at condition, but a has already been assigned while(_*_<100,20,a=_*_) => 81 // loop stopped at condition, before a was assigned a value
loop(num,expr(_),exit(_)?)
Evaluates expression expr, num number of times. code>expr receives _ system variable indicating the iteration.
loop(5, game_tick()) => repeat tick 5 times list = []; loop(5, x = _; loop(5, list += [x, _] ) ); list // double loop, produces: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], ... , [4, 2], [4, 3], [4, 4]]
In this small example we will search for first 10 primes, apparently including 0:
check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) ); primes = []; loop(10000, if(check_prime(_), primes += _ ; if (length(primes) >= 10, break()))); primes // outputs: [0, 1, 2, 3, 5, 7, 11, 13, 17, 19]
Higher Order Functions
map(list,expr(_,_i))
Converts a list of values, to another list where each value is result of an expression v = expr(_, _i)
where _ is passed as each element of the list, and _i is the index of such element. If break is called the
map returns whatever collected thus far. If continue and break are used with supplied argument, it is used in
place of the resulting map element, otherwise current element is skipped.
map(range(10), _*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
map(player('*'), _+' is stoopid') [gnembon is stoopid, herobrine is stoopid]
filter(list,expr(_,_i))
filters list elements returning only these that return positive result of the expr. With break and continue
statements, the supplied value can be used as a boolean check instead.
filter(range(100), !(_%5), _*_>1000) => [0, 5, 10, 15, 20, 25, 30]
map(filter(entity_list('*'),_=='Witch'), query(_,'pos') ) => [[1082.5, 57, 1243.5]]
first(list,expr(_,_i))
Finds and returns the first item in the list that satisfies expr. It sets _ for current element value,
and _i for index of that element. break can be called inside the iteration code, using its argument value
instead of the current item. continue has no sense and cannot be called inside first call.
first(range(1000,10000), n=_; !first( range(2, sqrt(n)+1), !(n % _) ) ) => 1009 // first prime after 1000
Notice in the example above, that we needed to rename the outer _ to be albe to use in in the inner first call
all(list,expr(_,_i))
Returns true if all elements on the list satisfy the condition. Its roughly equivalent
to all(list,expr) <=> for(list,expr)==length(list). expr also receives bound _ and _i variables. break
and continue have no sense and cannot be used inside of expr body.
all([1,2,3], check_prime(_)) => true
all(neighbours(x,y,z), _=='stone') => true // if all neighbours of [x, y, z] are stone
map(filter(rect(0,4,0,1000,0,1000), [x,y,z]=pos(_); all(rect(x,y,z,1,0,1),_=='bedrock') ), pos(_) )
=> [[-298, 4, -703], [-287, 4, -156], [-269, 4, 104], [242, 4, 250], [-159, 4, 335], [-208, 4, 416], [-510, 4, 546], [376, 4, 806]]
// find all 3x3 bedrock structures in the top bedrock layer
map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), [x,y,z]=pos(_);
all(rect(x,y,z,1,0,1),_=='bedrock') && for(rect(x,y-1,z,1,1,1,1,0,1),_=='bedrock')<8),
pos(_) ) => [[343, 3, -642], [153, 3, -285], [674, 3, 167], [-710, 3, 398]]
// ditto, but requiring at most 7 bedrock block in the 18 blocks below them
reduce(list,expr(_a,_,_i), initial)
Applies expr for each element of the list and saves the result in _a accumulator. Consecutive calls to expr
can access that value to apply more values. You also need to specify the initial value to apply for the
accumulator. break can be used to terminate reduction prematurely. If a value is provided to break or continue,
it will be used from now on as a new value for the accumulator.
reduce([1,2,3,4],_a+_,0) => 10 reduce([1,2,3,4],_a*_,1) => 24