## traditional for loop: for i = a to b (step c) possibilities

Get help with using AutoHotkey and its commands and hotkeys
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

### traditional for loop: for i = a to b (step c) possibilities

- I have been thinking about traditional for loops, and the best way to achieve them, since I first started using AutoHotkey. It was the one killer feature that Excel macros had, that AutoHotkey did not.
- While thinking about what the very best function/object solution might be (see the links lower down), I considered that using a while loop might be the least-worst solution, of all the solutions available.
- I always felt that Loop was doable/workable, but not quite good enough. The while loop gives a result that I find reasonably satisfactory.
- I find traditional for loops particularly useful for use with offsets with NumGet/NumPut/SubStr, plotting graphs (the values for the axes), and mathematics generally.
- I'm posting here in case anyone else has anything to say re. this.

Code: Select all

``````q:: ;traditional for loop possibilities
;-20, -16, ... 16, 20
while (vIndex := A_Index*4-4 -20) <= 20
MsgBox, % vIndex
MsgBox

;the while loop looks much better than this Loop equivalent
Loop
{
if !((vIndex := A_Index*4-4 -20) <= 20)
break
MsgBox, % vIndex
}
MsgBox

;although this Loop equivalent isn't too bad,
;it doesn't give you all of the information neatly at the top
Loop
{
vIndex := A_Index*4-4 -20
if !(vIndex <= 20)
break
MsgBox, % vIndex
}
MsgBox

;here's another possibility,
;although again, it doesn't give you all of the information neatly at the top,
;also we've had to change the inequality,
;i.e. 'has it gone beyond the end' to 'will it go beyond the end next time',
;and we have to use the step value in the inequality, which is a further disadvantage
Loop
{
vIndex := A_Index*4-4 -20
MsgBox, % vIndex
}	until (vIndex+4 > 20)
MsgBox

MsgBox, % "and now in reverse"

;do the for loop in reverse (a minus sign was added)
;--20, --16, ... -16, -20
;i.e. 20, 16, ... -16, -20
while (vIndex := A_Index*4-4 -20) <= 20
MsgBox, % -vIndex
MsgBox

;do the for loop in reverse (various changes made)
;20, 16, ... -16, -20
while (vIndex := -A_Index*4+4 +20) >= -20
MsgBox, % vIndex
return
``````

[traditional for loop via functions][for i = a to b (step c)]
Traditional For loop (i.e., step through a sequence) - Suggestions - AutoHotkey Community
https://autohotkey.com/board/topic/7122 ... -sequence/

[traditional for loop via objects][for i = a to b (step c)]
For loop question - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/6691 ... ntry423515
enum type and while loop - Ask for Help - AutoHotkey Community
https://autohotkey.com/board/topic/7886 ... hile-loop/
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Helgef
Posts: 4690
Joined: 17 Jul 2016, 01:02
Contact:

### Re: traditional for loop: for i = a to b (step c) possibilities

Hello jeeswg .
V2 only,

Code: Select all

``````x_to_y_step_k(byref i, x, y, k:=1){
return ( i := x+(a_index-1) ) <= y ? ( a_index += k-1, true ) : ( i -= k, false )
}
; example
while x_to_y_step_k(i, 5, 17, 3)
msgbox(i)
``````
You can do nested loops, you should but use different index.

Cheers.
derz00
Posts: 497
Joined: 02 Feb 2016, 17:54
GitHub: derz00
Location: Middle of the round cube

### Re: traditional for loop: for i = a to b (step c) possibilities

Very nice. Out of curiosity, what about v1 prevents that?
try it and see
...
Helgef
Posts: 4690
Joined: 17 Jul 2016, 01:02
Contact:

### Re: traditional for loop: for i = a to b (step c) possibilities

MsgBox(i)
Spoiler
Cheers.
derz00
Posts: 497
Joined: 02 Feb 2016, 17:54
GitHub: derz00
Location: Middle of the round cube

### Re: traditional for loop: for i = a to b (step c) possibilities

Sure enough. From v2-changes.htm:
The following built-in variables can be assigned values:

A_Index: For counted loops, modifying this affects how many iterations are performed.
try it and see
...
KuroiLight
Posts: 327
Joined: 12 Apr 2015, 20:24
GitHub: KuroiLight

### Re: traditional for loop: for i = a to b (step c) possibilities

you can create a range object and for loop over that:
while a range could be re-used and makes the code look cleaner overall, its likely slower than your while method.

Code: Select all

``````for _, i in range(5, 25) {
FileAppend, %i%`,, *
}
FileAppend, `n, *
for _, i in range(20, -20, 4) {
FileAppend, %i%`,, *
}
return

range(startx, endx, stepsize := 1) {
stepsize := stepsize * (startx < endx ? 1 : -1)
range_a := Array()
Loop {
range_a.Push(startx)
startx += stepsize
} Until ((stepsize > 0) ? (startx >= endx) : (startx <= endx))
range_a.Push(startx)
return range_a
}
``````
Windows 10, Ryzen 1600, 16GB G.Skill DDR4, 8GB RX 480 | [MyScripts][MySublimeSettings] [Unlicense][MIT License]
01/24/18
[/color]
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

### Re: traditional for loop: for i = a to b (step c) possibilities

- Cheers KuroiLight, I had forgotten about the Range function approach, until I looked through those links recently, looking for object enumerator examples.
- I generally like the Range function approach, because it's the simplest, but also because it can be useful for contexts outside of loops.
- Some tricky issues are that if there is a mismatch between the start/end values, and the sign of the step, what to do. My function ignores the sign of the step. Another issue is where the start and end values are the same (btw @KuroiLight you should check your function re. this). E.g. Range(1, 1, 1), should probably give an array containing 1.
- Btw my function is not necessarily the most efficient, so I welcome any suggestions/alternatives. Do notify of any issues.

Code: Select all

``````q:: ;a range function for for loops
for _, vValue in JEE_Range(5, 50, 5)
MsgBox, % vValue

vOutput := ""
for vKey, vValue in JEE_Range(5, 50, 5)
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput

vOutput := ""
for vKey, vValue in JEE_Range(-5, -50, 5)
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput

vOutput := ""
for vKey, vValue in JEE_Range(-100, 100, 10)
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput

vOutput := ""
for vKey, vValue in JEE_Range(0, 1, 0.1)
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput

vOutput := ""
for vKey, vValue in JEE_Range(0, 1, 1/16)
vOutput .= vKey " " vValue "`r`n"
MsgBox, % vOutput
return

JEE_Range(vStart, vEnd, vStep:=1)
{
if !vStep || (vStart + vEnd = "")
return {}
oArray := [], vNum := vStart
vDirection := (vNum <= vEnd) ? 1 : -1
vStep := vDirection * Abs(vStep)
Loop
{
oArray.Push(vNum)
;vNum += vStep
vNum := vStart + (vStep*A_Index)
if (vNum2 = vNum)
|| ((vDirection = 1) && (vNum > vEnd))
|| ((vDirection = -1) && (vNum < vEnd))
break
vNum2 := vNum
}
return oArray
}
``````
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Capn Odin
Posts: 1352
Joined: 23 Feb 2016, 19:45
GitHub: CapnOdin
Location: Denmark

### Re: traditional for loop: for i = a to b (step c) possibilities

I have never felt the need for something like this but I think that I would approach it like this

Code: Select all

``````for i in new ForLoop(0, 10, 2) {
res .= i " "
}

MsgBox, % res

Class ForLoop {
__New(i, endCondition, step) {
this.i := i
this.endCondition := endCondition
this.step := step
}

_NewEnum() {
return this
}

Next(ByRef key, ByRef val := "") {
if(res := this.i < this.endCondition) {
key := this.i
this.i += this.step
}
return res
}
}``````
Please excuse my spelling I am dyslexic.
Helgef
Posts: 4690
Joined: 17 Jul 2016, 01:02
Contact:

### Re: traditional for loop: for i = a to b (step c) possibilities

It is inefficient to calculate the whole range before the loop starts, in case you want to break the loop early, if you want to reuse the range it is fine. Here is another range function, where you specify the number of steps rather than step size, it always hits the end points,

Code: Select all

``````eqspace(a,b,n){	; creates an array of n equally spaced numbers between a and b (inclusive).
local
t:=(b-a)/(n-1)
e:=object(), e.SetCapacity(n)
loop n
e.push(a+(A_Index-1)*t)
return e
}
``````
and as a enumerator,

Code: Select all

``````class eqspace {
__new(a,b,n){ ; n integer
this.a:=a, this.n:=n, this.t:=(b-a)/(n-1), this.i:=0
}
_newenum(){
return this
}
next(byref k, byref v:=""){
v:=++this.i, k:=this.a+(v-1)*this.t
return v-1 != this.n
}
}
``````
More on the topic: How to add to the key of a For-loop

Cheers.
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

### Re: traditional for loop: for i = a to b (step c) possibilities

Range(Count) [unlike Python: starts at 1]
Range(Start, End, Step:=1) [unlike Python: includes End, accepts floats]

- I've modified my JEE_Range function slightly, to a point where I think its input/output behaviour is good enough for a built-in function.
- It's based on the Excel VBA for loop, 'For i = a To b [Step c]', which I find more intuitive/useful/newbie-friendly than the Python 'range' function.
- The function handles integers/floats for all 3 parameters (like Excel, unlike Python which only accepts integers for the 3 parameters).
- The start/end points are inclusive (like Excel, unlike Python which also starts at the start point, but oddly stops *before* the end point).
- You can specify one parameter only (like Python, unlike Excel which needs 2 parameters minimum). [E.g. 'for k, v in Range(3)' would be equivalent to AHK's 'Loop 3'.]
- If you specify one parameter only, that is treated as the end point, the start point is assumed to be 1 (unlike Python which assumes 0, unlike Excel which needs 2 parameters minimum).
- The sign of the step parameter is ignored, the direction is based on the start/end points. In Excel/Python the step parameter is always taken literally. [Excel/Python both allow a mismatch, and return a blank array, I'd prefer an error, or the behaviour I've chosen.]

- I've investigated Python's range function, and Coco's port, to double-check how they work.
Python's range() Function Explained | Python Central
https://www.pythoncentral.io/pythons-range-function-explained/
range() - for For-loops [AHK v1.1 and v2.0-a049] - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=4303
- I'm not fond of ending the loop before the end number is reached, or of starting at 0 by default. Note: in Python, if you swap the start/end numbers, you unintuitively get a different set of numbers.
for i in range(1,5): print(i) gives 1,2,3,4 [Python code]
for i in range(5,1,-1): print(i) gives 5,4,3,2 [Python code]
- Also, regarding the 'key, value' pair that AHK's for loop returns: it's more useful to return 'index, number' instead of just 'number', during a loop, furthermore, it allows you to create an AHK linear array via obj := Range(...).

- This function creates an array in advance, which is inefficient if you want to exit the loop early, and which requires memory to store the values.

Code: Select all

``````q:: ;test JEE_Range function
MsgBox, % JEE_Range(10).Length() ;10
MsgBox, % JEE_Range(-3, 3, 0.5).Length() ;13

vOutput := ""
for vKey, vValue in JEE_Range(10)
vOutput .= (A_Index=1?"":",") vValue
MsgBox, % vOutput

vOutput := ""
for vKey, vValue in JEE_Range(-3, 3, 0.5)
vOutput .= (A_Index=1?"":",") vValue
MsgBox, % vOutput

oArray := {}
oArray.Push([5, 50, 5])
oArray.Push([-5, -50, 5])
oArray.Push([-100, 100, 10])
oArray.Push([0, 1, 0.1])
oArray.Push([0, 1, 1/16])

vOutput := ""
for _, oTemp in oArray
{
vOutput := ""
for vKey, vValue in JEE_Range(oTemp*)
vOutput .= (A_Index=1?"":",") vValue
MsgBox, % vOutput
}
return

;==================================================

;unlike Python: starts at 1 by default
;unlike Python: includes vEnd (instead of stopping before vEnd)
;the sign of vStep is ignored (the direction is determined by checking vStart and vEnd)
;vStart/vEnd/vStep can be floats
;an AHK linear array is produced, starting at key 1

;JEE_Range(vEnd) [where vStart is 1, and vStep is 1]
;JEE_Range(vStart, vEnd) [where vStep is 1]
;JEE_Range(vStart, vEnd, vStep)

JEE_Range(vStart, vEnd:="", vStep:=1)
{
local
if (vEnd = "")
vEnd := vStart, vStart := 1
if !vStep || !JEE_IsNum(vStart) || !JEE_IsNum(vEnd) || !JEE_IsNum(vStep)
return {}
oArray := [], vNum := vStart + (vStep*0) ;ensures that the first number has the same type (Integer/Float) as the later numbers
oArray.SetCapacity(Ceil(Abs((vEnd-vStart)/vStep))+1) ;Floor may be sufficient, instead of Ceil
vDirection := (vNum <= vEnd) ? 1 : -1
vStep := vDirection * Abs(vStep)
vNum2 := ""
Loop
{
oArray.Push(vNum)
;vNum += vStep ;less reliable
vNum := vStart + (vStep*A_Index)
if (vNum2 = vNum)
|| ((vDirection = 1) && (vNum > vEnd))
|| ((vDirection = -1) && (vNum < vEnd))
break
vNum2 := vNum
}
return oArray
}

;==================================================

;e.g.:
;MsgBox(JEE_IsNum(1))
;MsgBox(JEE_IsNum("a"))

JEE_IsNum(vNum)
{
try vTemp := vNum + 0
catch
return 0
return !(vTemp = "")
}

;==================================================
``````
- This function uses an enumerator object to generate numbers on the fly.

Code: Select all

``````;==================================================

; q:: ;test JEE_RangeAlt
; vOutput := ""
; for vKey, vValue in JEE_RangeAlt(-5, 5, 0.5)
; 	vOutput .= vKey " " vValue "`r`n"
; MsgBox, % vOutput
; return

JEE_RangeAlt(oParams*) ;vStart, vEnd:="", vStep:=1
{
local
global RangeEnum
return new RangeEnum(oParams*)
}

class RangeEnum
{
__New(vStart, vEnd:="", vStep:=1)
{
if (vEnd = "")
vEnd := vStart, vStart := 1
;if !vStep || !JEE_IsNum(vStart) || !JEE_IsNum(vEnd) || !JEE_IsNum(vStep)
;	return ""
this.start := vStart
this.end := vEnd
this.direction := (vStart <= vEnd) ? 1 : -1
this.step := this.direction * Abs(vStep)
this.last := ""
this.index := 0
return this
}
_NewEnum()
{
return this
}
Next(ByRef k:="", ByRef v:="")
{
v := this.start + (this.step*this.index)
this.index++
k := this.index
if (this.last = v)
|| ((this.direction = 1) && (v > this.end))
|| ((this.direction = -1) && (v < this.end))
return 0
return 1
}
}

;==================================================
``````
[EDIT:] Added enumerator object version of function.
Last edited by jeeswg on 10 Jul 2019, 09:05, edited 4 times in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Posts: 1206
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

### Re: traditional for loop: for i = a to b (step c) possibilities

I think it would be great to have a For-Loop style as in C++ in AHKv2.
for (i := 0, i < 10, ++i)
Although it is true that a Range function could also be used outside the loop, however, in my case I never had the need.
I think something like this: mi_func(range(0,(i)=>i<10,1)), but I'm not sure in which scenario this can be useful.
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

### Re: traditional for loop: for i = a to b (step c) possibilities

- I'm glad that somebody likes that kind of loop! [EDIT: It has its charms now that I look at it more closely.]
- I don't know if I'll ever get used to it. My eyes still glaze over. I can't remember which bit goes where from memory.

Code: Select all

``````//some C++ code
for (int i = 1; i <= 10; ++i)
for (int i = 0; i < vec1.size(); ++i)
for (auto it = vec1.begin(); it != vec1.end(); ++it)
for (std::vector<std::string>::iterator it = vec1.begin(); it != vec1.end(); ++it)
``````
- Having done some conversions between various programming languages. The one good thing about a C++ for loop line, is that you can copy it into Java without making any changes (and vice versa). Cheers.
- Btw is your mi_func example similar to something you've seen?
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
nnnik
Posts: 4499
Joined: 30 Sep 2013, 01:01
Location: Germany

### Re: traditional for loop: for i = a to b (step c) possibilities

I don't see traditional for loops as a good thing - it's more like a hindrance to the language.
I don't see a big point in them. Many of the assumptions made at the time the for loop was created turned out to be incorrect.
And most of the time people just wanted to use the default implementation - count from 0 to n.

AHKs for loops are not suited for a range function atm.
Creating a new object in AHK is one of the slowest things that can happen.
Thats due to a decision regarding COM and AHK Objects.
One way we could make things better is by making for accept an enumerator directly and by calling the call method of the enumerators, rather than the next method.

The standard loop is perhaps the best solution for most AHK problems - I dont see a need to change that for now.
A for x in range(start, stop, step) syntax can be added later on given these requirements are fulfilled before the initial v2 release.

Pythons range function is a generator. We do not have the concept of generators in AHK.
It is perhaps one of the reasons why Python is so popular.
Recommends AHK Studio
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

### Re: traditional for loop: for i = a to b (step c) possibilities

- I find traditional for loops (Range) useful for:
- (specifying a variable name other than A_Index)
- starting at 0 (instead of A_Index-1)
- starting at n, and specifying an exit point (e.g. once I needed 6,7,8,9)
- drawing graphs e.g. -10 to 10, steps of 0.5
- (taking such things and modifying them a bit, in a way that's less easy when manipulating A_Index and the Loop count etc)

- I'm used to traditional for loops (Range) from Excel VBA, and I have always felt their lack in AutoHotkey.

- (Re. speed. For quick scripts, you save time when writing code. And for long-term scripts, you can replace the traditional for loop (Range).)

- @nnnik: I'd just highlight some points you made that would be worth expanding:
more like a hindrance to the language.

Many of the assumptions made at the time the for loop was created turned out to be incorrect.

Thats due to a decision regarding COM and AHK Objects.
- My ideal would be to have Range implemented now by the current means, still pretty fast, and people could optimise it later if they wanted to.
- Although I haven't heard of any plans to implement it.
- (It's always possible to use custom functions, but sometimes you want to share a quick script on the forum.)
Last edited by jeeswg on 19 May 2019, 19:13, edited 1 time in total.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
nnnik
Posts: 4499
Joined: 30 Sep 2013, 01:01
Location: Germany

### Re: traditional for loop: for i = a to b (step c) possibilities

- (Re. speed. For quick scripts, you save time when writing code. And for long-term scripts, you can replace the traditional for loop.)
The traditional for loop is by no means slow. Thats not what I meant. AHKs internal for loop suffers from the need to create an enumerator object - which is rather slow.
Regardless typing an traditional for loop will always take longer than a for in loop or a normal loop. I doubt I would use it at all.
more like a hindrance to the language.
Many of the assumptions made at the time the for loop was created turned out to be incorrect.
While the traditional for loop is pretty versatile and can be applied to any circumstance it has a few issues.
Writing: for (int x = 0; x<10; x++) gets annoying after the... yeah its just annoying actually.
Its unnecessary long for go from 0 - 9.

A problem lies in expecting a condition for continuing the for loop.
You cannot know wether the condition for continuing can be perfectly resolved at the head of the loop every time.
People will start building their code to suit this form and that can be problematic because it will turn into a habit - a potentially bad habit.

The default and most common case is that you want to go from value a to value b.
And once again most of the time you want to do that to go over the values of some sort of array.

If people thought of defining enumerators/iterators for this - that automatically tell a for ... in ... construct how to enumerator/iterate then the traditional for loop would barely see any use.
It also could have prevented a lot of bugs from happeneing by allowing the compiler extra checks. It would be more readeable and makes code more accessible.
That would have prevented many people from developing tools practices and habits revolving around the old for loop.

This might not seem like much but if you consider that people died and had all kinds of bad stuff happen to them due to software bugs you will look at this differently.

To summarize: traditional for loops are:
.. unnecessarily large and stuffed
.. strict and cause bad habits
.. are less readeable than for .. in ..
.. force the user to create new code and make reuse more difficult -> this increases bugs

Most of the cases you described would have been covered by a for x in range loop.
The only good reason to keep the for loop around is, that we had it around before in other languages. I personally think that it's time to let this one rest in piece.
Thats due to a decision regarding COM and AHK Objects.
I'm treading on thin ground here because I did not put a lot of effort into researching this.
Essentially lexikos once said that creating AHK Objects is slow because every object has to go through the IDispatcher.
When looking at the AHK Source I found that every AHK Object is in fact a COM Object.

Normally creating an Object is not much more work than setting a few bytes in the memory.
Optimally by compying a small sequence of bytes from one point in the memory to another.
Thats not really slow - even if you consider the need to align new memory - at least it should be faster than what AHK does.

So AHK has to do something more than this. Putting together what lexikos said about the IDispatcher and my knowledge of COM and the AHK Source I came to a conclusion.
When AHK creates a new object it informs the system that a new COM Object has been created.
Considering that COM is used for interprocess communications it may take the system a while to set the new object up properly.
After that is done the new object is ready for use.

There are many implications in doing that. I think it was the correct decision lexikos made considering the current AHK development process.
But I'm fairly certain that he will decide against that decision in the future. At least if AHK has a positive future.
Recommends AHK Studio
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

### Re: traditional for loop: for i = a to b (step c) possibilities

- Just for the record:
- I saw someone describe this or similar as a traditional for loop:

Code: Select all

``````'Excel VBA code
For i = 0 To 5 Step 0.5
MsgBox i
Next
``````
- I see Python's 'range' function as basically the same thing. And Coco's and my 'range' variants also. (What I'd hope AHK to have.)
- Flipeador mentioned a C++ style for loop.
- (And AutoHotkey has a for loop which retrieves key-value pairs from a linear/associative array.)
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Posts: 1206
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

### Re: traditional for loop: for i = a to b (step c) possibilities

Just to be clear, I do not propose to replace the current behavior, but add support for two types of behaviors.
Btw is your mi_func example similar to something you've seen?
Not that I remember.
nnnik
Posts: 4499
Joined: 30 Sep 2013, 01:01
Location: Germany

### Re: traditional for loop: for i = a to b (step c) possibilities

When I say traditional Loop I mean the strict C Style loop.
Recommends AHK Studio
Helgef
Posts: 4690
Joined: 17 Jul 2016, 01:02
Contact:

### Re: traditional for loop: for i = a to b (step c) possibilities

Being used to choose my own loop variables, when I first saw A_Index, I thought it was joke. At least it has the benefit of syntax highlighting and recogniseability.

Cheers.