• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

ForEach

JASS:
library ForEach
    
    //=======================================================================
    // This library provides high-level functions at your normal high-level
    // efficiency loss. Zinc has a built-in syntax which is very close to
    // this, but Zinc also lacks a ton of features only vJass can provide.
    //
    // The premise is this:
    //
    // For each (from 0 to the specified exit-point), do (function).
    // For range (start, end), do (function).
    // For range (start, end, stride), do (function).
    //
    // Start may be higher than end.  The stride will reverse itself in that
    // case. The stride defaults to 1 (i = i + 1).
    //
    // cJass, now deprecated due to lack of proper care, had the "define"
    // syntax which allowed optional parameters. Because of the lack of such
    // functionality, ForEach is split into three functions:
    //
    // ForEach - takes an integer for how many times the loop should iterate.
    // ForRange - takes a start and end parameter
    // ForRangeEx - takes a start, end and stride parameter
    //
    // The last parameter will always be the function to be iterated through.
    //
    // The functionality of ForEach is this: you give it a function, the
    // function must take an integer and return a boolean. A true boolean
    // continues the loop as normal, a false boolean exits the loop ahead of
    // schedule. You can decide which one you are more comfortable with by
    // adjusting the constant "EXITWHEN".
    //
    // If you are iterating through a stack and you need to increment or
    // decrement the current and end position, use:
    //
    // ForEach_RaiseStack()
    // ForEach_LowerStack()
    //
    // Alternatively, you may set ForEach_A and ForEach_B directly.  A is the
    // running index, B is the end-position.
    //
    // A ForEach call may be nested within another ForEach call.
    
    
    // Syntax:
    //
    // function theLoop takes integer i returns boolean
    //     call TriggerExecute(myTriggerArray[i])
    //     return true
    // endfunction
    // ...
    // call ForEach(10, theLoop)
    //
    // # This runs the function "theLoop" exactly 10 times (0..9).
    
    
    globals
        // If the looping function returns this value, the loop ends:
        public constant boolean EXITWHEN = false
        
        public integer A = 0
        public integer B = 0
    endglobals
    
    
    function ForEach_LowerStack takes nothing returns nothing
        set A = A - 1
        set B = B - 1
    endfunction
    
    function ForEach_RaiseStack takes nothing returns nothing
        set A = A + 1
        set B = B + 1
    endfunction
    
    
    function interface ForEachFunc takes integer i returns boolean
    
    
    function ForRangeEx takes integer start, integer end, integer stride, ForEachFunc func returns nothing
        local integer a = A
        local integer b = B
        set A = start
        set B = end
        if (start > end) then
            set stride = -(stride)
        endif
        loop
            exitwhen ((A == B) or (func.evaluate(A) == EXITWHEN))
            set A = A + stride
        endloop
        set A = a
        set B = b
    endfunction
    
    
    function ForRange takes integer start, integer end, ForEachFunc func returns nothing
        call ForRangeEx(start, end, 1, func)
    endfunction
    
    
    function ForEach takes integer end, ForEachFunc func returns nothing
        call ForRangeEx(0, end, 1, func)
    endfunction
    
    
endlibrary



JASS:
library Example initializer init requires ForEach
    
    globals
        group array Groups
    endglobals
    
    private function clear takes integer i returns nothing
        call GroupClear(Groups[i])
        return true
    endfunction
    
    function ClearUnitGroups takes nothing returns nothing
        call ForEach(bj_MAX_PLAYER_SLOTS, clear)
    endfunction
    
    private function fill takes integer i returns nothing
        call GroupEnumUnitsOfPlayer(Groups[i], Player(i), null)
        return true
    endfunction
    
    function FillUnitGroups takes nothing returns nothing
        call ForEach(bj_MAX_PLAYER_SLOTS, fill)
    endfunction
    
endlibrary

 
Last edited:
Thanks for the feedback. I added a constant EXITWHEN which allows the user to choose whether true or false should be the return value which breaks the loop.

I've also taken your naming advice. I changed ForEach2 to ForRange, ForEach3 to ForRangeEx. I think that's more descriptive...

ForEach_Increase has been changed to ForEach_RaiseStack
ForEach_Decrease has been changed to ForEach_LowerStack

The description has been updated to shed more light on what this system is intended to do.
 
Ehm, on finalized libraries, I don't really see any library ever using this myself o-o. I mean, it can be nice if you want like super dynamism on a lib or something, but yea ;o. Even then, loops are always best done within a function as a loop is much faster than a loop of trigger evaluations : P. This was an issue I was running into with Spawn's onSpawn /cry. When I want to loop over things, I just pass a stack of values now = ).
 
A program that runs 1 time per hour to handle a baby process is not going to care. An engine is going to need fast speed, which is why good engines are written as low-level as possible to make ends meet. A high-level language is often written for fun and practical application; it's easy to type, easy to read and easier to follow than a noisy, repeatedly-used script.
 
Do you get what it does?

It takes the annoying and repetitive loop construct in JASS and converts it into something natural;

JASS:
library LoopTest initializer init requires ForEach
    
    
    function Loop takes integer i returns boolean
        call BJDebugMsg(I2S(i))
        return true
    endfunction
    
    function init takes nothing returns nothing
        call ForEach(11, Loop)
    endfunction
    
    
    // A very natural sequence, very easy to read, as opposed to:
    
    
    function BJDebugMsg takes string msg returns nothing
        local real i = 0    // you have to loop up here to find the start-pos
                            // because JASS only permits top-declarations.
        loop
            call DisplayTimedTextToPlayer(Player(i),0,0,60,msg)
            
            set i = i + 1   // annoying to type and easy to forget.
            
            exitwhen i == bj_MAX_PLAYERS
            // I always have to look twice to see if it's a greater than,
            // less than or equal to exitwhen. Take this example, the guy
            // loops through and displays the message even for computer play-
            // ers, which (you'd think) would have been spotted before the
            // game was released had it been a more readable context.
            // 
        endloop
    endfunction
    
endlibrary
 
The functionality has been adjusted to be the more common approach to a range-loop. Before, if you put a value of 10 into ForEach, the loop ran 11 times. I have changed this so the input-number will never be reached.

So if the user puts a variable into ForEach, that variable could be zero and the loop should not run for that value in such a case.

ForEach(1, func) will run "func" exactly 1 time, with the first value of 0.
 
Right, they have an iterable as a parameter. I could say ForEach(Range(10), func), but JASS wrappers can only take static parameters (no optional parameters). So ForEach(Range(0, 10), func) could not work if ForEach(Range(0, 10, 1), func) could also work.

I think, judging by feedback so far and after many evaluations myself, I don't think JASS is a suitable language to handle this kind of cool iteration.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
Far as I know, not even vJass allows you to pass arrays into a function :(

Would be nice to allow a syntax like:

function myfunc takes integer array i returns nothing
endfunction

Gosh, if only JASS were more like Python or Perl.
I'm not sure if this is a viable solution, but you could use types to create dynamic arrays.
JASS:
scope ThisCoolArray initializer Init

    type MyCoolArray extends integer array[15]
    
    globals
        MyCoolArray Cool
    endglobals
    
    private function Ehe takes MyCoolArray cool returns nothing
        set cool[0] = 199283
    endfunction
    
    private function Init takes nothing returns nothing
        set Cool = MyCoolArray.create()
        call BJDebugMsg(I2S(Cool.size)) // Returns 15
        call BJDebugMsg(I2S(Cool[0])) // Returns 0
        call Ehe(Cool)
        call BJDebugMsg(I2S(Cool[0])) // Returns 199283
    endfunction
    
endscope
 
Top