Functions and Global Variables
Functions and Global Variables
I have a sizeable block of code that I need to use in a few different places. Ordinarily I'd just copy and paste the code into the various sections where it's needed (because it's simpler and I know it will just work).
However, I thought I'd try using a function for the first time, and now I have a couple of issues that I could use some help with.
1. My code is triggered by a hotkey, but within that code is another hotkey that can be used depending on the outcome of the first hotkey's action. When I moved this code into a function, AHK complained about the code containing a hotkey, and so I had to dismantle that section to take it out.
2. Now that the code is in 2 separate sections, many of my variables no longer work. So I found the specific ones that needed to be shared across the code blocks and made them global. This mostly worked. But there is one instance where it doesn't.
I have a set of different conditions (each wrapped in its own braced section) that are all similar. I've assigned the same output variable name to each section, so that whichever one gets used, the output var is the same name and can therefore be referenced elsewhere regardless of which code block generated the output.
This no longer works now the code is split, and I can't make the various output vars global because there'd be multiple instances of the same name (which AHK won't accept). And if I have to go through and rename them all to be unique then it's no longer a case of reading one output variable to get my result (which the next hotkey then acts upon).
Does anyone have any tips or general suggestions on how I should be structuring things to get round issues such as this?
I can't post the actual code because it contains a lot of personal info and it would take ages to strip it all out for public posting. So I'm just looking general guidelines here.
However, I thought I'd try using a function for the first time, and now I have a couple of issues that I could use some help with.
1. My code is triggered by a hotkey, but within that code is another hotkey that can be used depending on the outcome of the first hotkey's action. When I moved this code into a function, AHK complained about the code containing a hotkey, and so I had to dismantle that section to take it out.
2. Now that the code is in 2 separate sections, many of my variables no longer work. So I found the specific ones that needed to be shared across the code blocks and made them global. This mostly worked. But there is one instance where it doesn't.
I have a set of different conditions (each wrapped in its own braced section) that are all similar. I've assigned the same output variable name to each section, so that whichever one gets used, the output var is the same name and can therefore be referenced elsewhere regardless of which code block generated the output.
This no longer works now the code is split, and I can't make the various output vars global because there'd be multiple instances of the same name (which AHK won't accept). And if I have to go through and rename them all to be unique then it's no longer a case of reading one output variable to get my result (which the next hotkey then acts upon).
Does anyone have any tips or general suggestions on how I should be structuring things to get round issues such as this?
I can't post the actual code because it contains a lot of personal info and it would take ages to strip it all out for public posting. So I'm just looking general guidelines here.
Re: Functions and Global Variables
It sounds like you have a scope issue. ahk has somewhat sensible scope rules.
functions by default don't get to access outside variables.
functions by default don't get to access outside variables.
Examples of ways to access variables in functions.As the function has Local scope so it will only be able to see the Super Global variable var_3Code: Select all
var_1 := "AutoHotkey" var_2 := "----------" Global var_3 := "ahk" ; Super Global
The line Global var_1 allows the use of var_1 but not var_2Code: Select all
fun_1() { MsgBox, % var_1 "`n" var_2 "`n" var_3 }
The line Global changes the scope of the function to Global allowing the use of both var_1 and var_2Code: Select all
fun_2() { Global var_1 MsgBox, % var_1 "`n" var_2 "`n" var_3 }
Instead of changing the scope the values can be passed as parameters to the functionCode: Select all
fun_3() { Global MsgBox, % var_1 "`n" var_2 "`n" var_3 }
Code: Select all
fun_4(var_1, var_2, var_3) { MsgBox, % var_1 "`n" var_2 "`n" var_3 }
Re: Functions and Global Variables
Without code to inspect, I can only make general suggestions.I have a set of different conditions (each wrapped in its own braced section) that are all similar....
I can't post the actual code because it contains a lot of personal info and it would take ages to strip it all out for public posting. So I'm just looking general guidelines here.
My first thought, based on you description is to use classes/objects. This will allow variable names that are the same, accommodate similar functionality with very little repeated code lines, modular design, and the ability to reach the individual objects and their members globally as desired.
If you are not familiar with classes/objects (OOP), it's a much different way to handle data and functionality, but has many advantages. If you are experimenting with different styles of coding anyway, I would suggest giving this a go. It will change the way you code and open many new possibilities.
The disadvantage of AHK (v1, not sure about v2) is that the members of the class (static) and instances are not clearly separated. In other words, a static call acts like just another object/instance (except that instances share access to the static members as well). This is much different than most languages where the class does not have access to instance methods. Static members have a clear separation, by default. I felt the need to mention this "gotcha" up front, because it will make your head spin the first time you see it.
Re: Functions and Global Variables
Thank you so much @Chunjee and @andymbody for your detailed replies. They were both incredibly helpful, especially seeing as you had so little concrete information to work with.
I have now managed to get the code fully working! A couple of different approaches worked:
One was making the 2 non-working variables Super Global (I made them global outside of the function).
And the other approach worked by not trying to put those two particular variables into another variable specifically for global use.
What I mean by that is that I thought it might be useful to easily identify global variables, because generally I don't want things to have a wider scope than necessary to avoid unintended outcomes. So I decided to use only names starting with an underscore for any variable that I made global (an easy rule to implement, seeing as I have only just started using functions and global variables for the first time).
Instead of going through all the individual code blocks to add an underscore to multiple instances of the existing variables that needed to be made global, I just did something like this:
There were only 6 that needed to be accessed outside of the function. This meant that I didn't need to mess with my existing code. However, it only worked for the first 4 variables. The last 2 stubbornly refused to be assigned any new variable name. But if I just did this instead, then they all worked. I have no idea why.
There's nothing particularly different about the last 2 variables, they're generated using pretty similar methods to the others. It's kind of frustrating not knowing why something doesn't work in what appears to be almost identical circumstances, but I'm just grateful that I got it working at all. I have spent so much time on troubleshooting this that I was close to just duplicating the entire code block in the several places where it was needed, which thankfully I don't have to do now, thanks to you guys and your help.
I will definitely look into classes and objects too (testing with a much less complicated code sample) because it sounds very flexible. Of course that's assuming that I can even understand how to use it based on the Help file, which is not exactly the most newbie-friendly resource.
Before that though, I might first try to figure out why the last two variables can't be seen outside the function when put into a different variable name. That's still bugging me, even though I have a workaround that fixes the issue.
I have now managed to get the code fully working! A couple of different approaches worked:
One was making the 2 non-working variables Super Global (I made them global outside of the function).
And the other approach worked by not trying to put those two particular variables into another variable specifically for global use.
What I mean by that is that I thought it might be useful to easily identify global variables, because generally I don't want things to have a wider scope than necessary to avoid unintended outcomes. So I decided to use only names starting with an underscore for any variable that I made global (an easy rule to implement, seeing as I have only just started using functions and global variables for the first time).
Instead of going through all the individual code blocks to add an underscore to multiple instances of the existing variables that needed to be made global, I just did something like this:
Code: Select all
Global _FileText := FileText
Global _InsertedText := InsertedText
Global _CurrentItem := CurrentItem
Global _SelectionTotal := SelectionTotal
Global _TimestampedText := TimestampedText
Global _TimestampedValue := TimestampedValue
There were only 6 that needed to be accessed outside of the function. This meant that I didn't need to mess with my existing code. However, it only worked for the first 4 variables. The last 2 stubbornly refused to be assigned any new variable name. But if I just did this instead, then they all worked. I have no idea why.
Code: Select all
Global _FileText := FileText
Global _InsertedText := InsertedText
Global _CurrentItem := CurrentItem
Global _SelectionTotal := SelectionTotal
Global TimestampedText
Global TimestampedValue
I will definitely look into classes and objects too (testing with a much less complicated code sample) because it sounds very flexible. Of course that's assuming that I can even understand how to use it based on the Help file, which is not exactly the most newbie-friendly resource.
Before that though, I might first try to figure out why the last two variables can't be seen outside the function when put into a different variable name. That's still bugging me, even though I have a workaround that fixes the issue.
Re: Functions and Global Variables
What do you mean by variable "name"?The last 2 stubbornly refused to be assigned any new variable name
You may be aware of this, but... for clarification...
Code: Select all
; This...
Global _TimestampedText := TimestampedText
; means...
_TimestampedText := ; the value found in (TimestampedText) at this particular point an time
; so... if
TimestampedText := "text"
; then...
_TimestampedText := TimestampedText
; means...
_TimestampedText := "text" ; but only at this particular moment
; ...if we later change the value of TimestampedText...
TimestampedText := "new text"
; then...
_TimestampedText := "text" ; is still the same value as originally assigned
; They are completely separate variables and are not linked in any way within your code
; SO...
; the global declaration is probably the equivalent to...
Global _TimestampedText := TimestampedText := ""
; which would mean...
_TimestampedText := "" ; throughout all of your code unless you put another value into it manually
Andy
Re: Functions and Global Variables
Oh, wow. Thank you.
I have completely misunderstood what Global _TimestampedText := TimestampedText means and how it works.
I have several braced code blocks that are triggered under different conditions. They all end with one of variables being TimestampedText. So when I do a command like ControlSetText, %EditField%, %TimestampedText% it displays the output of whichever code block was used in that particular instance. If another code block is used next time (e.g. code block b instead of code block a), then block b's output will be 'in' the TimestampedText variable, so that is the output that will be displayed this time when using ControlSetText.
There are multiple TimestampedText throughout the code, but only one _TimestampedText which I made global. I wrongly assumed that by doing _TimestampedText := TimestampedText I was telling AHK to duplicate whatever is in TimestampedText into _TimestampedText. So I thought whenever TimestampedText got updated, then _TimestampedText would also update to reflect that change, and I could then just read _TimestampedText from other functions which can no longer read the original variables ever since I had to split the code to make it into a function.
I assumed that it worked like this because I am getting correct output for the first 4 variables that I listed in my previous post. Clearly I've just accidentally got an outcome that I was seeking despite my variable assignments essentially being trash. I thought I was making progress, but it's now clear that I still don't know what the hell I'm doing! lol.
I have completely misunderstood what Global _TimestampedText := TimestampedText means and how it works.
I have several braced code blocks that are triggered under different conditions. They all end with one of variables being TimestampedText. So when I do a command like ControlSetText, %EditField%, %TimestampedText% it displays the output of whichever code block was used in that particular instance. If another code block is used next time (e.g. code block b instead of code block a), then block b's output will be 'in' the TimestampedText variable, so that is the output that will be displayed this time when using ControlSetText.
There are multiple TimestampedText throughout the code, but only one _TimestampedText which I made global. I wrongly assumed that by doing _TimestampedText := TimestampedText I was telling AHK to duplicate whatever is in TimestampedText into _TimestampedText. So I thought whenever TimestampedText got updated, then _TimestampedText would also update to reflect that change, and I could then just read _TimestampedText from other functions which can no longer read the original variables ever since I had to split the code to make it into a function.
I assumed that it worked like this because I am getting correct output for the first 4 variables that I listed in my previous post. Clearly I've just accidentally got an outcome that I was seeking despite my variable assignments essentially being trash. I thought I was making progress, but it's now clear that I still don't know what the hell I'm doing! lol.
Re: Functions and Global Variables
This is what you should be aware of...So I thought whenever TimestampedText got updated, then _TimestampedText would also update to reflect that change
By default, variables within AHK (and most languages) are assigned VALUES (characters and numbers). This is called assigned ByValue
- W := "water"
b := W ----> "water"
b becomes the same value as W (at this particular time) (the value is copied from W to b, like copy/paste)
b and w are different variables, like you and your father are different people. They reference DIFFERENT locations in memory
- W := "water"
b := &W -----> reference the SAME memory location (the bucket) that holds the value of "water"
b becomes a pointer to the memory location used by W that holds the value of "water". Therefore the two are "linked" because they REFER to the SAME memory location
MsgBox % b ----> will display a number that represents the memory location for W - 366478738
MsgBox % StrGet(b) ----> will display the value stored at that memory location - "water"
Unfortunately, (as far as I know) you cannot mix global scope with function scope. So, a global variable cannot be assigned the reference (memory location) of a non-global variable.
Andy
Re: Functions and Global Variables
Thanks for clarifying the distinction between ByValue and ByRef.
So given that I now have 2 separate functions, and the second function needs to be able to read the value of TimestampedText (and a few other variables) from within the first function (from whichever code block happened to 'fill' the variable) what would be your recommended way to share those variables between functions, rather than the haphazard way that I'm doing it at the moment?
By the way, I looked at objects in the AHK help file, and while they seemed really useful, it was way over my head. The description was so conceptual and abstract. I think I've got to learn to walk before I try running.
So given that I now have 2 separate functions, and the second function needs to be able to read the value of TimestampedText (and a few other variables) from within the first function (from whichever code block happened to 'fill' the variable) what would be your recommended way to share those variables between functions, rather than the haphazard way that I'm doing it at the moment?
By the way, I looked at objects in the AHK help file, and while they seemed really useful, it was way over my head. The description was so conceptual and abstract. I think I've got to learn to walk before I try running.
Re: Functions and Global Variables
Three general ways to share variables
1. Use global variables - shared variables that need to be maintained for any length of time, and passed around regularly.
2. Pass variables as arguments during function calls - used to provide input to a function or manipulate the variable data using an operation found in the function. These are usually just brief exchanges of data between two parties.
3. Using file read/writes - use only if you can't do 1 or 2 or to also provide permanent storage of values.
If I understand your case correctly... global variables would be best
Yes, classes and OOP can be a challenge to grasp at first. Once you "get it", they make all the sense in the world, but until then, they can make your head spin. To be honest, they are actually like real world objects, and we use the concept in the real world everyday, but don't realize it. I had trouble with understanding them at first (coming from years of procedural and top-down coding), but once the AHAH! moment happened, I was hooked... I turn to them regularly now. As a matter of fact, some languages are 100% OOP, which forced me to get busy with it.By the way, I looked at objects in the AHK help file, and while they seemed really useful, it was way over my head. The description was so conceptual and abstract. I think I've got to learn to walk before I try running.
If you decide to explore them at some point, try not to pay too much attention to all the jargon (instantiation, inheritance, polymorphism, etc), until you have the basics down. This stuff is important but not enough in the beginning to get bogged down with it. You first need to understand functions and transition into using them regularly. Nearly everything I do is using functions. Functions (called methods in OOP) are an absolute must with OOP. You can't do anything without them.
Sounds like you have quite the project on your hands... I wish you luck with it!
Andy