Skip to content

Latest commit

 

History

History
150 lines (111 loc) · 6.84 KB

LoopsAndHigherOrderFunctions.md

File metadata and controls

150 lines (111 loc) · 6.84 KB

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, expr), while(cond, limit, expr)

Evaluates expression expr repeatedly until condition cond becomes false, but not more than limit times (if limit is specified). Returns the result of the last expr evaluation, or null if nothing was successful. Both expr and cond will received a bound variable _ indicating current iteration, so its a number.

while(a<100,a=_*_) => 100 // loop stopped at condition
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