@swagfag
In short, you're asking me to demonstrate how to initialize during the first call to the enumerator function,
with some additional contrived requirements.
otherwise u could just use the return {__Enum: ...} pattern
Is that some kind of problem? What bearing does this have on the ultimate goal? It is
always possible to use the
{__Enum: ...} pattern, whether or not the use of that pattern brings any advantages.
to not create additional objects
What are
additional objects? If you compare grp and grpx closely, you should find that grp creates not only an additional object with an __Enum method, but
an additional closure as well:
- grp(xs) -> {__Enum} -> (_,a) captures xs -> (rs*) captures xs, a, i
- grpx(xs) -> (rs*) captures xs, i
So in this case, the non-__Enum method creates fewer objects.
to not access any object properties(otherwise u could just query VarRefs.Length instead).
What bearing does this requirement have on the topic? It seems your goal is not to demonstrate initialization, but to perform micro-optimization. In that case, the requirement should be to meet some performance criteria, not to avoid one or two specific things that you perceive as adding overhead.
I assume this requirement came about because grpx accesses
rs.length on each iteration whereas grp does not. But did you notice that grpx
also accesses
xs.length on every iteration whereas grp does it up front? They are not directly comparable, and these differences are not inherent to the use or disuse of __Enum.
i managed to do it using static, which works fine as long as there's only 1 for-loop accessing the iterator at a time
To support nested for-loops, each for-loop
must have its own state, not a single shared state. The example using
static demonstrates only that
static should not be used for this; it has nothing to do with whether you use __Enum. There are
generally two ways to ensure each loop gets its own enumerator state:
- Explicitly create a new state, such as by calling a function (e.g. ObjOwnProps, grpx and grp).
- Use __Enum.
Regardless of whether you use __Enum, there are multiple ways to represent or store the state, including:
- Use a closure.
- Use a bound function (for mutable state you would bind an object or VarRef).
- The classic way: create an object with a Next (v1) or Call (v2) method.
There are also "dirtier" methods, such as relying on the value of A_Index or the for-loop variables.
Although the majority of enumerators require at least one object per enumeration, you can avoid creating
additional objects by utilizing a closure, as with grpx. If you want to avoid accessing properties more than once, you can do the same thing as with
static i, but using a non-static captured variable instead.
Code: Select all
loopAsManyTimesAsThereAreVars() {
i := 0
next(VarRefs*) {
if !i
i := VarRefs.Length + 1
return --i
}
return next
}
for a, b, c in loopAsManyTimesAsThereAreVars() {
MsgBox 'Outer loop index: ' A_Index
if (A_Index = 2) {
for a, b, c, d, e in loopAsManyTimesAsThereAreVars()
MsgBox 'Inner loop index: ' A_Index
}
}
or
Code: Select all
loopAsManyTimesAsThereAreVars() => (
i := 0,
(VarRefs*) => i ? --i : i := VarRefs.Length
)
for a, b, c in loopAsManyTimesAsThereAreVars() {
MsgBox 'Outer loop index: ' A_Index
if (A_Index = 2) {
for a, b, c, d, e in loopAsManyTimesAsThereAreVars()
MsgBox 'Inner loop index: ' A_Index
}
}
If you want to pass the same object directly to multiple for-loops but give each loop its own state, you generally need __Enum. For example (putting aside the original goal of demonstrating initialization), this can be used with or without
():
Code: Select all
loopAsManyTimesAsThereAreVars() => loopAsManyTimesAsThereAreVars
loopAsManyTimesAsThereAreVars.__Enum := (f,n) => (*) => n--