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

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

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

19 Jan 2018, 03:45

- 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
Links:

[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/
Helgef
Posts: 3298
Joined: 17 Jul 2016, 01:02
Contact:

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

19 Jan 2018, 12:04

Hello jeeswg :wave: .
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.
User avatar
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

19 Jan 2018, 12:22

Very nice. Out of curiosity, what about v1 prevents that?
try it and see
...
User avatar
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

19 Jan 2018, 12:40

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
...
User avatar
KuroiLight
Posts: 327
Joined: 12 Apr 2015, 20:24
GitHub: KuroiLight

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

19 Jan 2018, 13:50

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
}
*edit* I see one of your links has something similar.
Windows 10, Ryzen 1600, 16GB G.Skill DDR4, 8GB RX 480 | [MyScripts][MySublimeSettings] [Unlicense][MIT License]
01/24/18
[/color]
User avatar
jeeswg
Posts: 5411
Joined: 19 Dec 2016, 01:58
Location: UK

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

31 Jan 2018, 01:43

- 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
}
User avatar
Capn Odin
Posts: 1290
Joined: 23 Feb 2016, 19:45
Location: Denmark

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

31 Jan 2018, 02:42

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: 3298
Joined: 17 Jul 2016, 01:02
Contact:

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

31 Jan 2018, 04:14

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.
User avatar
jeeswg
Posts: 5411
Joined: 19 Dec 2016, 01:58
Location: UK

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

15 Sep 2018, 10:35

- I've modified my JEE_Range function slightly, to a point where I think its functionality is good enough for a built-in function.
- It does create an array in advance, which is inefficient if you want to exit the loop early.
- It's based on (but slightly different to) the Excel VBA for loop, which I find more intuitive/useful/newbie-friendly than the Python 'range' function.
- 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-ra ... 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.
- Also, regarding the 'key, value' pair that AHK's for loop returns: it's more useful to return 'index, number' instead of just 'number', that way you can create a linear array.

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
;the sign of vStep is ignored (the direction is determined by checking vStart and vEnd)
;vStep can be a float
;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)
;JEE_Range(vStart, vEnd, vStep)

JEE_Range(vStart, vEnd:="", vStep:=1)
{
	if (vEnd = "")
		vEnd := vStart, vStart := 1
	if !vStep || !JEE_IsNum(vStart) || !JEE_IsNum(vEnd)
		return {}
	oArray := [], vNum := vStart + (vStep*0) ;ensures that the first number has the same type (Integer/Float) as the later numbers
	vDirection := (vNum <= vEnd) ? 1 : -1
	vStep := vDirection * Abs(vStep)
	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 = "")
}

;==================================================
[EDIT:]

Code: Select all

;before:
	oArray := [], vNum := vStart
	;...
	if !(Floor(vStep) = vStep)
		vNum += 0.0
;after:
	oArray := [], vNum := vStart + (vStep*0) ;ensures that the first number has the same type (Integer/Float) as the later numbers
User avatar
Flipeador
Posts: 1019
Joined: 15 Nov 2014, 21:31
GitHub: Flipeador
Location: Argentina
Contact:

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

18 Sep 2018, 12:12

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.
Windws 1♂ Pro 64-Bits I make scripts for AHKv2 (my v2 compiler) & WIN_7+ Spanish Argentina SublimeText 3 & AHKv2 My GDI+ Library
User avatar
jeeswg
Posts: 5411
Joined: 19 Dec 2016, 01:58
Location: UK

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

18 Sep 2018, 12:33

- 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?
User avatar
nnnik
Posts: 3526
Joined: 30 Sep 2013, 01:01
Location: Germany

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

18 Sep 2018, 12:49

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
User avatar
jeeswg
Posts: 5411
Joined: 19 Dec 2016, 01:58
Location: UK

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

18 Sep 2018, 13:16

- I find traditional for loops 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 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.)

- @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.)
User avatar
nnnik
Posts: 3526
Joined: 30 Sep 2013, 01:01
Location: Germany

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

18 Sep 2018, 14:07

- (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
User avatar
jeeswg
Posts: 5411
Joined: 19 Dec 2016, 01:58
Location: UK

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

18 Sep 2018, 14:30

- 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.)
User avatar
Flipeador
Posts: 1019
Joined: 15 Nov 2014, 21:31
GitHub: Flipeador
Location: Argentina
Contact:

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

18 Sep 2018, 14:41

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.
Windws 1♂ Pro 64-Bits I make scripts for AHKv2 (my v2 compiler) & WIN_7+ Spanish Argentina SublimeText 3 & AHKv2 My GDI+ Library
User avatar
nnnik
Posts: 3526
Joined: 30 Sep 2013, 01:01
Location: Germany

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

18 Sep 2018, 14:50

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

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

21 Sep 2018, 02:49

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.

Return to “Ask For Help”

Who is online

Users browsing this forum: 8bitgunny, Google [Bot], swagfag, Xtra and 104 guests