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.