0

I know the newer version is better, but company does not allows me to. So the question is related to AutoHotKey, ver 1.0.47.06.

I am trying to refactor my 400 lines program, by separating them into functions.

CaseNumberArray := "" ; The array to store all the case numbers
CaseNumberArrayCount := 0    

; Helper function to load the case number into the array
ReadInputFile() {
    Loop, Read, U:\case.txt
    {
        global CaseNumberArrayCount
        CaseNumberArrayCount += 1 ; Increment the ArrayCount
        CaseNumberArray%CaseNumberArrayCount% := A_LoopReadLine
        current := CaseNumberArray%CaseNumberArrayCount% 
    }
}

CreateOutputHeader()
ReadInputFile()

MsgBox, There are %CaseNumberArrayCount% case(s) in the file.

Loop, %CaseNumberArrayCount% 
{
    case_number := CaseNumberArray%A_Index%
    MsgBox, %case_number%
}

The last part of the code is testing if I can retrieve the case numbers I loaded into the array named CaseNumberArray, but it is currently all blank.

I studied this question, the author user1944441 wrote:

Important: YourArray must not be global and the counter in YourArray%counter% must not be global, the rest doesn't matter.

I experimented by placing the global variables in different location, but it still does not work. I know the CaseArrayCount is correctly stored, and the Read Loop is working as well (When it is outside of a function). Is it possible to separate the code into a function?

1 Answer 1

2

Usually, global/local declarations are placed right below the method header, not somewhere in some subsequent code block. After all, these declarations apply only to the entire function.
You have to distinguish between simple loop counter variables and variables holding the actual size of the array. In your code, CaseNumberArrayCount describes the size of CaseNumberArray whereas in the answer to which you're referring, it's a counter only used to iterate over the array, which might as well be local.
But you don't have to use two "variables" anyway. Your pseudo array (which can be accessed like CaseNumberArray1, CaseNumberArray2, CaseNumberArray2, ...) has an unused CaseNumberArray0, why not not store the size there?
A pseudo array is actually a collection of sequentially numbered variables. global CaseNumberArray (which by the way you didn't seem to try) will only allow access to the variable named CaseNumberArray, but not CaseNumberArray1 or CaseNumberArray2 and so on.
One solution would be to use Assume-global mode which makes every global variable accessible by default:

; Now, CaseNumberArray0 will hold the array length,
; rendering CaseNumberArrayCount unnecessary
CaseNumberArray0 := 0 

; Helper function to load the case number into the array
ReadInputFile() {
    ; We want to access every global variable we have,
    ; beware of name conflicts within your function!
    global
    Loop, Read, test.txt
    {
        CaseNumberArray0 += 1
        CaseNumberArray%CaseNumberArray0% := A_LoopReadLine
    }
}

; Here's an alternative: Let AHK build the pseudo array!
ReadInputFileAlternative() {
    global caseAlt0
    FileRead, fileCont, test.txt
    StringSplit, caseAlt, fileCont, `n, `r
}

ReadInputFile()

out := ""
Loop, %CaseNumberArray0% 
{
    out .= CaseNumberArray%A_Index% "`n"
}

MsgBox, There are %CaseNumberArray0% case(s) in the file:`n`n%out%

; Now, let's test the alternative!
ReadInputFileAlternative()
out := ""
Loop, %caseAlt0% 
{
    out .= caseAlt%A_Index% "`n"
}

MsgBox, There are %caseAlt0% case(s) in the alternative pseudo-array:`n`n%out%

Edit: "Real Arrays"

As suggested in the comments, here's what I would do instead: I would convince my boss to allow the use of an up-to-date version of AHK and then work with real arrays. This comes with several benefits:

  1. Real arrays are fully managed by AHK, which means that things like inserting, removing, iterating and indexing can all automagically be done by AHK.
  2. A real array resides in one real variable, meaning that you can pass it along functions and anywhere you want, without having to worry about the current scope and whether you can access it in the first place.
  3. The array syntax is very similar to most other languages, making your code intuitive and easier to read. And maybe it helps you in the future when dealing with another language.
  4. Primitive n-dimensional arrays (and primitive AHK objects in general) can be expressed using JSON. This provides you with an easy way to (de-)serialize AHK objects.

The following code snippet shows the two methods used above (reading loop and splitting), but with real arrays. You will notice that we don't need any global declarations anymore, since we now can declare the array inside our function, and simply pass it back to the caller. In my opinion, this is what functions should really look like: A "black box" that doesn't affect its surroundings.

; Method 1: Line by line
ReadLineByLine(file) {
    out := []
    Loop, Read, % file
    {
        out.Insert(A_LoopReadLine)
    }
    return out
}

; Method 2: StrSplit
ReadAndSplit(file) {
    FileRead, fileCont, % file
    return StrSplit(fileCont, "`n", "`r")
}

caseNumbers := ReadLineByLine("test.txt")
out := "ReadLineByLine() yields " caseNumbers.MaxIndex() " entries:`n`n"
; using the for loop
for idx, caseNumber in caseNumbers
{
    out .= caseNumber "`n"
}
MsgBox % out


caseNumbers := ReadAndSplit("test.txt")
out := "ReadAndSplit() yields " caseNumbers.MaxIndex() " entries:`n`n"
; using the normal loop
Loop % caseNumbers.MaxIndex()
{
    out .= caseNumbers[A_Index] "`n"
}
MsgBox % out

MsgBox % "The second item is " caseNumbers[2]
Sign up to request clarification or add additional context in comments.

2 Comments

Great answer and they made a lot of sense, I just need to get back to the work computer and give the code a try. Because my office uses old version of AHK. Thanks for the answer, I'll return!
oh and this may be an even better learning opportunity, show the object syntax and that objects actually have no scope, they are always passed down by reference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.