Local vs Global
Local vs Global
Can someone please try to explain to me how these differ in practice. I've heard that one should avoid global variables, but it's hard for me to envision not needing access to a variable at various places throughout a script. I may be misunderstanding the whole concept though. 
Development System : Imac G5, MorphOs 3.19
Re: Local vs Global
I think this is one of the most important things to learn when starting to code. You should try to use Local variables as much as possible, and especially in loops and other places where variable contents are accessed frequently. Data should be delivered to/from functions with arguments and return values rather than with Global variables. Try to make functions re-usable so that they don't need globals from a certain project, but could be used in any project.
Of course you can't totally avoid using globals, but they should rather contain some more static data like settings values or otherwise commonly needed data... and for totally static data you should use Constants instead of global variables. Depends of the project, but if you're starting to have tens of global variables, I'd say you're doing something wrong... roughly said
What makes it a bit confusing is that different programming languages have different defaults for variables that you don't declare local or global. For example, in Python "x = 1" (without declaring it local or global) makes the variable x local, but in Lua x becomes global, so it's very important to check which way each programming language behaves regarding this. And remember this if you convert code from a programming language to other language.
Luckily it's quite simple after all, and Hollywood documentation does a good job describing it. Read these two chapters and you should understand it pretty well: Global variables and Local variables
Of course you can't totally avoid using globals, but they should rather contain some more static data like settings values or otherwise commonly needed data... and for totally static data you should use Constants instead of global variables. Depends of the project, but if you're starting to have tens of global variables, I'd say you're doing something wrong... roughly said
What makes it a bit confusing is that different programming languages have different defaults for variables that you don't declare local or global. For example, in Python "x = 1" (without declaring it local or global) makes the variable x local, but in Lua x becomes global, so it's very important to check which way each programming language behaves regarding this. And remember this if you convert code from a programming language to other language.
Luckily it's quite simple after all, and Hollywood documentation does a good job describing it. Read these two chapters and you should understand it pretty well: Global variables and Local variables
Re: Local vs Global
Ok. Thanks.
I kind of understand, but what am I doing wrong here:
Because I only get NIL in return. (I'm just playing around here.)
I kind of understand, but what am I doing wrong here:
Code: Select all
Function p_CharName()
local a_string = ("\"So, what's your name?\"")
SetFont(#SERIF, 20)
SetFontStyle(#BOLD|#ANTIALIAS)
Locate(8, 380); Place the cursor position for our text output using Print()
Print("Your slumber is brutally annihilated by someone violently bursting in through the door. As you desperately try to orient yourself you hear the voice of a young man:")
Locate (GetAttribute(#DISPLAY, 1, #ATTRRAWWIDTH ) / 2 - strlen(a_string) * 5 , 450)
print(a_string)
p_GetName()
Locate (GetAttribute(#DISPLAY, 1, #ATTRRAWWIDTH ) / 2 - strlen(a_string) * 5 , 500)
print (name)
EndFunction
Function p_GetName(name)
local names = {"Billy", "Tom", "Jerry", "Luke", "Roger"}
local name = rnd(TableItems(names))
Return (name)
EndFunctionDevelopment System : Imac G5, MorphOs 3.19
Re: Local vs Global
Functions only return a value, not a variable or its name.
So, in the p_CharName() function you haven't declared the name variable and the return value from p_GetName() isn't used for anything. Change it to:
Local name = p_GetName()
Then the value returned from the function is actually placed in the "name" variable, that is also declared as local now. You could declare it earlier without giving it a value too. Or use the return value from the function without even placing it in a variable. And Hollywood's style guide tells you to use $ at the end of string variable names.
Examples to choose from:
1)
Local name$ = p_GetName()
print(name$)
2)
Local name$
name$ = p_GetName()
print(name$)
3)
print(p_GetName())
BTW. The variable name used in p_CharName doesn't need to be the same as in p_GetName. Local variable names are only used in the function they're declared, only things that are passed are values. So, it could be playername$ in p_CharName and name$ in p_GetName etc..
And then the p_GetName() function... you don't need the function parameter in this case, because you're not giving any arguments to the function. So the declaration line could just be:
Function p_GetName()
rnd(TableItems(names)) gives you just a number, and to make it pick the corresponding name from the names table would look like this:
Local name$ = names[Rnd(TableItems(names))]
Return (name$)
So, in the p_CharName() function you haven't declared the name variable and the return value from p_GetName() isn't used for anything. Change it to:
Local name = p_GetName()
Then the value returned from the function is actually placed in the "name" variable, that is also declared as local now. You could declare it earlier without giving it a value too. Or use the return value from the function without even placing it in a variable. And Hollywood's style guide tells you to use $ at the end of string variable names.
Examples to choose from:
1)
Local name$ = p_GetName()
print(name$)
2)
Local name$
name$ = p_GetName()
print(name$)
3)
print(p_GetName())
BTW. The variable name used in p_CharName doesn't need to be the same as in p_GetName. Local variable names are only used in the function they're declared, only things that are passed are values. So, it could be playername$ in p_CharName and name$ in p_GetName etc..
And then the p_GetName() function... you don't need the function parameter in this case, because you're not giving any arguments to the function. So the declaration line could just be:
Function p_GetName()
rnd(TableItems(names)) gives you just a number, and to make it pick the corresponding name from the names table would look like this:
Local name$ = names[Rnd(TableItems(names))]
Return (name$)
Re: Local vs Global
Excellent! Thanks. This helps a lot!
This seems to work as intended:
This seems to work as intended:
Code: Select all
Function p_CharName()
local a_string = ("\"So, what's your name?\"")
local name$ = p_GetName()
SetFont(#SANS, 20)
SetFontStyle(#BOLD|#ANTIALIAS)
Locate(8, 380); Place the cursor position for our text output using Print()
Print("Your slumber is brutally annihilated by someone violently bursting in through the door. As you desperately try to orient yourself you hear the voice of a young man:")
Locate (GetAttribute(#DISPLAY, 1, #ATTRRAWWIDTH ) / 2 - strlen(a_string) * 5 , 450)
print(a_string)
p_GetName()
Locate (GetAttribute(#DISPLAY, 1, #ATTRRAWWIDTH ) / 2 - strlen(name) * 5 , 500)
print (name$)
EndFunction
Function p_GetName(name)
local names = {"Billy", "Tom", "Jerry", "Luke", "Roger"}
Local name$ = names[Rnd(TableItems(names))]
Return (name$)
EndFunctionDevelopment System : Imac G5, MorphOs 3.19
Re: Local vs Global
Yeah, but remove that lonely "p_GetName()" after the "print(a_string)" line, because it does nothing.
Re: Local vs Global
Aha! Because:
Code: Select all
local name$ = p_GetName()Brilliant, thanks again!
Development System : Imac G5, MorphOs 3.19
Re: Local vs Global
Yes, the p_GetName() function is called at that point and whatever the function will return is stored immediately in the local name$ variable to be used later in the p_CharName() function.oceanarts wrote: ↑Mon Nov 25, 2024 9:49 pm Aha! Because:Will run that function anyway, right?Code: Select all
local name$ = p_GetName()
Also one small detail in this case, because you don't need to give any arguments to the p_GetName() function (you don't call it like p_GetName("blabla")), you can replace the "Function p_GetName(name)" line with the "Function p_GetName()" line. If you have that "name" there, it will create a local "name" variable in the p_GetName() function, but there's no code that would use it for anything.
These both would be practically the same if it helps to understand:
Code: Select all
Function p_GetName()
local names = {"Billy", "Tom", "Jerry", "Luke", "Roger"}
Local name$ = names[Rnd(TableItems(names))]
Return (name$)
EndFunctionCode: Select all
Function p_GetName(name$)
local names = {"Billy", "Tom", "Jerry", "Luke", "Roger"}
name$ = names[Rnd(TableItems(names))]
Return (name$)
EndFunctionRe: Local vs Global
Here is an example to you about why you should use Local Functions all the time:
Here is first the LOCAL version:
Idea with this is, that when doing "FOR n = 1 to 2", most of time the N itself doesn't have much matter, and therefore it is usually most sensible to just name the same all the time, and common naming is to use variable "n" (=number) to make code easily readable.
For this reason it is not unusual that you might have several For N = 1 to 2 inside your code that are being executed inside each other, as in:
While you might not have it exactly the way I just showed, it will usually happen in practice, because you are having "FOR N = 1 to 2", and inside that loop there is call to another function, which also has "FOR N = 1 to 2", resulting in practice the above happening.
In the example Func1(), Func2() case the idea is that you are doing Func1(), that is calling Func2(), now you want to keep track of the progress for example, and therefore you debugprint(n) in the Func1(), which results in 1, 2, 3, 4... and you are able to see how the loop is progressing for example.
Using LOCAL things work nice, but, now, lets take a look at GLOBAL version:
Now "FOR N = 1 to 2" are not using LOCAL variables, and therefore they are GLOBAL instead.
This results in couple of unexpected behaviors.
First is that since Debugprint(n:"..n) is done AFTER returning from Func2(), since Func2() also has a Global N, instead of N being value of 1, 2, 3... as intended, it is actually the last value that N was when returning from Func2().
Another unexpected thing is that by quick look you would then think that N should be 5 then, right? No, because idea is that each FOR loop increases the value of N by 1, and if it gets past the number 5, then it wont execute the FOR loop anymore, but continue forward.
So N is first 1, and For loop is executed, then it is 2, 3, 4, 5 and FOR loop is executed, then it becomes 6, and FOR loop is NOT executed, and continues forward from FOR loop, ending the Function and returning back to Func1().
And now value of N is 6, which can be seen when Func1() debugprint value of N out.
The next unexpected behavior that happens is that when Func1() FOR loop is now NEXT executed, value of N becomes 7, and then it goes to FUNC2() which again makes N be For 1 to 5, ending it at value 6 again, and again returns to Func1(), Debugprints value of N (=6) and again NEXT executed, N becomes 7, meaning it is in an endless loop, never getting out of that Func1 FOR loop, since everytime it returns from Func2() value of N is 6, and then NEXT execution makes it value of 7, and again executing Func2() making it again become 6.
This is an example of a bug that is very easy to get into your code when you start making bigger programs, and also hard to catch, since while this one ends into an endless loop, more likely you will just get a wrong value, like lets change the example to this (changing numbers in FOR in Funcs()):
What happens now is that instead of being executed 5 times as intended, now it executes only 1 time, since Func2() makes the N become 11 (which is also completely unexpected result for N value for Func1() which is expected to at max be 5)
Endless loop is not so bad, since you notice that your software is stuck somewhere, and just find where it is stuck, but situation like this is much worse to hunt down, since you don't really know where exactly the problem was that might have resulted in a bug only much later in the code.
Here is first the LOCAL version:
Code: Select all
Function Func1()
For Local n = 1 To 10
Func2()
DebugPrint("n:"..n)
Next
EndFunction
Function Func2()
For Local n = 1 To 5
/* do stuff */
Next
EndFunction
Func1()For this reason it is not unusual that you might have several For N = 1 to 2 inside your code that are being executed inside each other, as in:
Code: Select all
For N = 1 to 2
For N = 1 to 2
For N = 1 to 2
Next
Next
Next
In the example Func1(), Func2() case the idea is that you are doing Func1(), that is calling Func2(), now you want to keep track of the progress for example, and therefore you debugprint(n) in the Func1(), which results in 1, 2, 3, 4... and you are able to see how the loop is progressing for example.
Using LOCAL things work nice, but, now, lets take a look at GLOBAL version:
Code: Select all
Function Func1()
For n = 1 To 10
Func2()
DebugPrint("n:"..n)
Next
EndFunction
Function Func2()
For n = 1 To 5
/* do stuff */
Next
EndFunction
Func1()
This results in couple of unexpected behaviors.
First is that since Debugprint(n:"..n) is done AFTER returning from Func2(), since Func2() also has a Global N, instead of N being value of 1, 2, 3... as intended, it is actually the last value that N was when returning from Func2().
Another unexpected thing is that by quick look you would then think that N should be 5 then, right? No, because idea is that each FOR loop increases the value of N by 1, and if it gets past the number 5, then it wont execute the FOR loop anymore, but continue forward.
So N is first 1, and For loop is executed, then it is 2, 3, 4, 5 and FOR loop is executed, then it becomes 6, and FOR loop is NOT executed, and continues forward from FOR loop, ending the Function and returning back to Func1().
And now value of N is 6, which can be seen when Func1() debugprint value of N out.
The next unexpected behavior that happens is that when Func1() FOR loop is now NEXT executed, value of N becomes 7, and then it goes to FUNC2() which again makes N be For 1 to 5, ending it at value 6 again, and again returns to Func1(), Debugprints value of N (=6) and again NEXT executed, N becomes 7, meaning it is in an endless loop, never getting out of that Func1 FOR loop, since everytime it returns from Func2() value of N is 6, and then NEXT execution makes it value of 7, and again executing Func2() making it again become 6.
This is an example of a bug that is very easy to get into your code when you start making bigger programs, and also hard to catch, since while this one ends into an endless loop, more likely you will just get a wrong value, like lets change the example to this (changing numbers in FOR in Funcs()):
Code: Select all
Function Func1()
For n = 1 To 5
Func2()
DebugPrint("n:"..n)
Next
EndFunction
Function Func2()
For n = 1 To 10
/* do stuff */
Next
EndFunction
Func1()Endless loop is not so bad, since you notice that your software is stuck somewhere, and just find where it is stuck, but situation like this is much worse to hunt down, since you don't really know where exactly the problem was that might have resulted in a bug only much later in the code.
Re: Local vs Global
Thank you for the tips, Bugala.
Another question, though...
I see the name$ being made available to p_CharName(), but this is the type of data that should be available across the code in multiple functions. How would that be done while avoiding using a Global variable?
Another question, though...
Code: Select all
Function p_CharName()
local a_string = ("\"So, what's your name?\"")
local name$ = p_GetName(); This calls the p_GetName Function and stores the return.
SetFont(#SANS, 20)
SetFontStyle(#BOLD|#ANTIALIAS)
Locate(8, 380); Place the cursor position for our text output using Print()
Print("Your gentle slumber is brutally annihilated by someone violently bursting in through the door. As you desperately try to orient yourself you hear the voice of a young man:")
Locate (GetAttribute(#DISPLAY, 1, #ATTRRAWWIDTH ) / 2 - strlen(a_string) * 5 , 450)
print(a_string)
Locate (GetAttribute(#DISPLAY, 1, #ATTRRAWWIDTH ) / 2 - strlen(name$) * 16 , 500)
print ("Uh... ", name$)
EndFunction
Function p_GetName()
local names = {"Billy", "Tom", "Jerry", "Luke", "Roger"}
Local name$ = names[Rnd(TableItems(names))]
Return (name$)
EndFunction
Development System : Imac G5, MorphOs 3.19