Author |
Topic: Scope of Variables (Read 520 times) |
|
Zaphod
Guest
|
 |
Scope of Variables
« Thread started on: Apr 10th, 2016, 4:13pm » |
|
Scope.
When we talk about the scope of a variable we are stating where it is accessible. A variable with Global scope if available throughout the entire program. A variable with Local scope is accessible only to those parts of the program that are within the same code block as the variable.
Here are the words of the Author of BB4W on the subject. Quote:Variables are global by default in BBC BASIC, so any variable that you have not explicitly declared as LOCAL or PRIVATE, and is not a formal parameter, is global (see below). Sometimes, for clarity, I add a REMark listing the global variables accessed by a function or procedure, but that's only for my benefit.
Note: Variable scoping in BBC BASIC is similar to what it is in C, i.e. variables can be seen by any 'inner' function unless they are explicitly declared as local. However, being an interpreted language the scope is dynamic (determined at run time) rather than the static lexical scoping of C. |
|
Variables can be created in many ways as are listed here. http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin2.html#creation If a function or procedure has parameters sent to it the variables in the parameter list of the definition become local variables. If you need a refresher on Procedures and Functions it might be best to re-read this first. http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin2.html#procedures
DEF FNtest(a%,mystring$) a% and mystring$ become local to this function.
Only this function or any that it calls know about those variables and once the function returns or a procedure ends the local variables cease to exist. If a Function (or procedure) needs a new variable for processing within that function then it should declare that variable as LOCAL. Code:
DEF FNtest(a%,mystring$)
LOCAL new%, old%
Why does scope matter? The main use of limiting the scope of variables is so that you can keep the local set distinct and isolated from the main global set. You can make code modular and ensure that the modules can work in any program by limiting the way information flows into and out of the function or procedure. A procedure can be a thought of as black box to the calling program and all the information flows via the parameter list and the returned values. Global variables defined within a procedure would be like holes in the box that information could leak in or out of in an uncontrolled way. If we use a global variable within a procedure, one that was defined in the main program, say, we can have no idea what information it contains or even if it is appropriate to change it. If the information comes through the parameter list then unless we take pains to make it so to we cannot affect the values being supplied. And the local variable is a separate variable to the one in the calling program. We pass the information from one main code variable to the local variable where it gets used.
Result=FNtest(First%,Some$)
If we want the calling variable to take on the value of the local value then we would have to use RETURN in the parameter list definition. This called passing by reference. Pass by value and pass by reference is another topic!
DEF FNtest(RETURN a%,mystring$)
In this case any changes we make to a% will be returned to First%. Local variables are the key to making code reusable and so save us a lot of effort. We need to be a little bit careful though as if I call a second procedure from the first, and this first procedure defined a variable as LOCAL, or has it sent to it as a parameter, the second procedure would see this as being global. It was defined outside of it but it has no idea at what stage. Code:
PROCone(Mystring$)
END
DEF PROCone(a$)
LOCAL I%
PROCtwo(a$)
...
ENDPROC
DEF PROCtwo(b$)
LOCAL I%
...
ENDPROC
If the second procedure has variables defined as LOCAL (even if they have the same name as in the first procedure!) then the first procedure cannot know about them, they are again local only to the entity that causes them to be created and its children. This takes a moment of thought. Local can have lots of levels, but local is very localized.
In the last example Mystring$, a$ and b$ may all exist at the same time and have the same content, but they remain different variables. Mystring$ is global to PROCone() and PROCtwo(), a$ is global to PROCtwo(). There is a tendency in such circumstances not to pass a$ on as a parameter to the child procedure but to use it as a global. If you KNOW that the PROCtwo is never going to have a use other than in this context that is fine, in my opinion. But you have lost the ability to extract PROCtwo and use it elsewhere without caution. Perhaps you would write this as a reminder: Code:
DEF PROCtwo : REM a$ is global.
a$=MID$(a$,2)
...
ENDPROC
In the Cross Reference utility there is a category of "Shared variables that are not global." What this means is that a procedure has defined a variable but not defined it as being local so it is in fact global, and then it has been used again by another procedure. This is flagged as it might well be a mistake and generally we try not to share globals. That is a rookie kind of thing before you discover local variables. It does mean that you will get warnings if you write a procedure just to define constants and initialize a program, which is not a bad thing to do but which upsets the Cross Referencer. It tries to help but in this instance tells you things that you knew already and kind of confuses the issue.
So perhaps some code examples. Code:
A1%=1
B1%=12
C1%=5
PRINT FNmean(A1%,B1%,C1%)
END
DEF FNmean(a%,b%,c%)
LOCAL sum, mean
sum=a%+b%+c%
mean=sum/3
=mean
It is long winded to make a few points and could be made much more compact, but let's ignore that. A1%, B1% and C1% are Gobal. They are defined in the main program. a%, b% and c% are local because they are in the definition's parameter list. mean and sum are local because we used the LOCAL keyword to make them so. If you open the List Variables utility and run this program I doubt if you will see anything except the Static variables and A1%, B1% and C1%, because the others come into existence only temporarily as local variables and are destroyed as being no longer needed when the function has finished. a% is not A1%. The value of A1% is transferred to a%. Don't make the formal parameter in the definition A1% as well or you will have two variables briefly in existence with the same name. Now that may not matter with such a trivial program but there are circumstances when this goes wrong because of these things called events. The code that ON SYS, ON TIME ON XXX points to. When an event happens the code jumps to some other location and processes code and then returns. You would have to be very careful to ensure that the correct variable gets used. And then again you are more likely to get confused as to which you are dealing with than the computer. A1% would have one value and A1% would have another, see confusing! For more on this complication see. http://www.bbcbasic.co.uk/bbcwin/manual/bbcwin6.html#interrupts There are very good arguments for using an event queue and serializing events is a manner similar to those in Eventlib to avoid those difficulties.
The ideal situation then is to pass information that is global to the procedure via the parameter list doorway. (Not chuck it over the fence at it.) If you want that global value changed by the procedure you will put a RETURN before it in the DEF statement and pass it by reference. The value is then passed back at the end to the calling procedure. Otherwise the global is passed by value and the global remains unchanged no matter what you do with its value in the procedure. Any other variables you need to process that information you define as LOCAL within that procedure. They remain just long enough to do their job and get annihilated at the end. You can use the same local variable name in many different procedures. I tend to use I% as the counter for looping structures. If I see and I% or J% in a procedure I expect it to have some count and be something like the index of an array. It gives instant meaning as to what is likely to be happening. But you can call you can call your counters and any variable anything you like, so long as it does not start with a keyword. It is quite all right to use the Static Variables as LOCAL. I know they are upper case but they are an exception to the naming convention on global names. Static variables are quick to access and compact. If you look at the library code you will see they are often used.
Z
|
|
Logged
|
|
|
|
|