PDA

View Full Version : visibility of variables



danbaron
07-10-2011, 07:45
I'm using PBCC6, and I have a function which declares a lot of local variables.

The function then calls other subroutines.

I would like to be able to use the local variables declared in the function, in the subroutines, without explicitly passing them to the subroutines.

Since variables declared as "local", exist while a function is active, I hoped that these variables would be visible to the subroutines that the function calls.

I hoped that I could rely on a concept, which I think is referred to as, "shadowing".

But, it seems to me, that it cannot be done.

Here is a simplified example of what will not work.


' code -----------------------------------------------------------------------------------------

#Compile Exe
#Dim All

Sub s2()
x += 1
End Sub

Sub s1()
Local x As Long
x = 1
s2()
Print x
End Sub

Function PBMain () As Long
s1()
WaitKey$
End Function

' compiler output ------------------------------------------------------------------------------

Error 519 in C:\Users\root\Desktop\Toom\test.bas(5:001): Missing declaration: X
Line 5: x += 1

(Incidentally, you cannot copy the above compiler output to the clipboard.)

But, I don't think my wish is so unreasonable.

As shown below, you can do it in Perl.

I guess, since neither $x or $y is declared in f3(), Perl goes "up the chain" and looks in f2(), etc.


' code -----------------------------------------------------------------------------------------

sub f1
{
local $x = 1;
f2();
print "$x\n";
}

sub f2
{
local $y = 4;
$x += 1;
f3();
print "$y\n";
}

sub f3
{
$x += 1;
$y += 1;
}

f1();

' output ---------------------------------------------------------------------------------------

5
3

Charles Pegge
07-10-2011, 10:56
Hi Dan,

you have at least 2 choices in PB

The first is to create a UDT (user defined type) to hold all the local variables you want to share. Then pass this BYREF to the subroutines.

The second is to put the subroutines inside the main function using the traditional Basic GOSUB syntax. PB uses colon labels ... RETURN for these.
This is the most efficient option but disfavoured in current programming practice.

Charles

John Spikowski
07-10-2011, 16:58
GOTO/GOSUB may be old school but still very useful even today and continues to be supported in ScriptBasic.

I like ScriptBasic's approach to FUNCTION/SUB scope of variables. By default all variables are global. Variables passed as arguments to FUNCTION/SUB routines are LOCAL. You may use the LOCAL keyword to isolated additional variables used within the routine if so desired. SB also has a couple of option flags that can be set to require all variables be initialized (assigned) before the script runs and all variables within a FUNCTION/SUB be LOCAL by default. SB also supports MODULE based scope where global variables are only global to the module. They can still be accessed from the main code by using the module name as a prefix.

PRINT MyModule::some_module_variable

Petr Schreiber
07-10-2011, 17:43
Hi Dan,

I would be interested in some less model-like example to see the use of shadowing, do you have any link by hand?

Regarding your problem, that is, achieving shadowing, I think PowerBASIC multiline MACROs could come handy.
Complete explanation of what MACRO is you can find in the help file, your model case would look like this, let me know:


#COMPILE EXE
#DIM ALL

MACRO s2()
x += 1
END MACRO

MACRO s1()
LOCAL x AS LONG
x = 1
s2()
TXT.PRINT x
END MACRO

FUNCTION PBMAIN () AS LONG
DIM hWin AS DWORD
TXT.WINDOW("Dan example", 10, 10 ,25, 80) TO hWin
s1()
TXT.WAITKEY$
TXT.END
END FUNCTION


And here the conversion of PERL sample:


#COMPILE EXE
#DIM ALL

MACRO f1
LOCAL x AS LONG
x = 1
f2()
TXT.PRINT "x=", x
END MACRO

MACRO f2
LOCAL y AS LONG
y = 4
x += 1
f3()
TXT.PRINT "y=",y
END MACRO

MACRO f3
x += 1
y += 1
END MACRO

FUNCTION PBMAIN()
DIM hWin AS DWORD
TXT.WINDOW("Perl 2 PB", 10, 10 ,25, 80) TO hWin

f1()

TXT.WAITKEY$
TXT.END
END FUNCTION

Please note I used TXT pseudoconsole, as I do not have PB/CC but just PB/WIN 10. The TXT pseudoconsole works in both, so it is easier to port code from one language to other.

I must admit I am a bit scared by the shadowing, I can see it causing debugging hell when used improperly or by accident, but that is up to the programmer how he manages the order in his code, I guess :)



(Incidentally, you cannot copy the above compiler output to the clipboard.)

Ctrl-C does not work, but when you mark the text via mouse and use right click/Copy, it is placed in the clipboard, just tried it.


Petr

danbaron
07-10-2011, 20:19
Thanks guys.

I guess I should indicate how this problem arose.

I have been trying to implement Toom-Cook (Toom-3, more specifically) multiplication.

http://en.wikipedia.org/wiki/Toom–Cook_multiplication (http://en.wikipedia.org/wiki/Toom%E2%80%93Cook_multiplication)

I didn't read the explanation of it as carefully as I should have.

I finished making the subroutines, my next step would have been trying to get them to work - debugging.

If you are multiplying two integers, each of n digits, then, the time required for grade school multiplication is proportional to n^2.

For Toom-3 (as you can read), the time required is approximately n^1.465.

It turns out that makes a big difference as n grows.

n^2 / n^1.465 = n^0.535.

We can call it, n^0.5.

So, for instance, if n = 10,000, then, the time to do a multiplication by the grade school method should be approximately 100 times the time to do it by the Toom-3 method.

I was riding on the train, when I realized something was wrong.

It concerns the section in the article, "Pointwise Multiplication".

I didn't read that section correctly.

There are 5 required multiplications.

I implemented them by the grade school method.

That way is wrong.

Each of the numbers you are multiplying will have n/3 digits.

That means that the lower limit on the time required to multiply two n digit integers would be 5*n^2/9.

In that case, the method is no good.

For any value of n, you would only save 4/9 the time by using Toom-3.

On the train, I was convinced the article was wrong, but, I was wrong.

The section says that these multiplications must also be implemented by Toom-3, the method must be recursive.

So, the many arrays of structures which I had originally implemented as global, would now have to be local to a function which would be recursively invoked.

That is why I posted about this topic.

I thought, I should be able to just move my global declarations into a function, and then the function can call the subroutines I already made, which are constructed on the assumption that the variables they use are global, the variables are not passed as parameters. I hoped, that if I declared the variables as local to the function, and so they would exist while the function was active, that I would not have to pass them to the subroutines, the variables would have sort of a sub-global scope. But, things don't work that way. (Incidentally, I don't see anything wrong with the concept of sub-global scopes. Maybe it would be hard to implement.)

So, the model is of a function which is called recursively and which has a lot of variables which other subroutines need to operate on. I see how Petr's macros would work in a different situation.

To me, John's scoping of variables in ScriptBasic, is substantially better than what exists in PowerBasic.

I think I will keep going, using the UDT, like Charles said.

Dan

Charles Pegge
08-10-2011, 04:33
the concept of sub-global scopes

This is where OOP is especially useful. The properties (variables) of an object are visible to all its methods (functions), but invisible to those of another object.

Charles

John Spikowski
08-10-2011, 04:52
To me, John's scoping of variables in ScriptBasic, is substantially better than what exists in PowerBasic.

Thanks Dan for the positive comment about ScriptBasic. Peter Verhas (author) did a wonderful job with the language and I wish he was still contributing to the project. Armando's efforts should also be commended.

José Roca
09-10-2011, 04:03
I hoped, that if I declared the variables as local to the function, and so they would exist while the function was active, that I would not have to pass them to the subroutines, the variables would have sort of a sub-global scope.


What kind of local variables would be then?



This is where OOP is especially useful. The properties (variables) of an object are visible to all its methods (functions), but invisible to those of another object.


Local variables are only visible to the method where they have been declared. Otherwise, they won't be local.

John Spikowski
09-10-2011, 06:06
Does PB not allow global variables within a FUNCTION/SUB?

danbaron
09-10-2011, 06:39
In PowerBasic there is one global namespace.

In, for instance, both ScriptBasic and C there are also module or file namespaces.

If I remember correctly, in C++ you can have as many namespaces as you want.

Concerning local variables in PowerBasic, it depends on what is meant by, "local".

It can mean, "only visible from within the subroutine", and/or, "only existing while the subroutine is active".

If it just means the latter, then, I don't think it is unreasonable to hope that, if, for instance, Subroutine A calls Subroutine B, then, the local variables in Subroutine A would be visible from Subroutine B, since, Subroutine A is active while Subroutine B is invoked.

In PowerBasic, global variables are visible to every subroutine and function. So, it seems to me that the extension of this idea would be to have the local variables of subroutines and functions, visible to every "smaller branch on the limb", or, equivalently, to every "further link in the chain".

You can see the idea clearly in the Perl code above.

To me, having just one global namespace, and all other variables (unless passed as parameters) visible only in the subroutine or function in which they are declared, is quite primitive.

I am surprised that by now, PowerBasic has not at least adopted the ideas of modules, which, in my opinion would be quite an improvement.

If I am correct, for awhile, PowerBasic was Turbo Basic. Turbo Pascal implemented "units" (I think, like modules), and separate compilation, in 1987.

http://en.wikipedia.org/wiki/Turbo_Pascal

Only beginning in 2011 has PowerBasic introduced SLLs, which implement separate compilation (for the console compiler).

But, as far as I know, it still has not implemented variables which are visible only to a particular compilation unit.

I don't know much about SLLs, but, it is not apparent to me that they are as useful as modules/units.

(Even the 1990 specification for Fortran, has modules.)

------------------------------------------------------------------------------

I didn't know about, "right-click copy", so, now I do.

Maybe shadowing would create debugging hell, but, I still think there is room for improvement in the scoping gradations of PowerBasic.

Out of curiosity, I'm wondering if anyone else ever uses a debugger. I never do, only print statements.

------------------------------------------------------------------------------

And, I still don't understand why PowerBasic's IDE seems static from version to version.

I find it hard to believe that they are working so hard on new features, that they can't spare the time to make some improvements in the IDE.

For instance, just the slightest thing, like having the option of doing a replacement only in the selected text.

And, maybe I'm dreaming, but, the availability of regex replacements would be very nice.

danbaron
09-10-2011, 07:08
I don't think so (PBCC6).

The code, as shown below, compiles.

But, if either of the lines, "Global i As Int", or, "Global j As Int", is included in the code (not commented out), then, it won't compile, and you get the error message,

Error 524 in C:\Users\root\Desktop\Toom\globals.bas(#:###): Undefined TYPE
Line #: Global # as Int


' code -----------------------------------------------------------------------------------------

#Compile Exe
#Dim All

'Global i As Int

Sub s()
'Global j As Int
End Sub

Function PBMain () As Long
Global k As Int
End Function

Petr Schreiber
09-10-2011, 09:07
Hi Dan,

even the code you posted does not compile - simply because INT is not datatype in PowerBASIC. INT in powerBasic, as well as thinBasic, is function to "convert a numeric expression to an integral value".
When you need integer variable in PowerBASIC, you have this possible variants:

INTEGER - for 16bit
LONG - for 32bit
QUAD - for 64bit

The available datatypes are documented in PB/Help file under "Data Types" root chapter. Integral datatypes (both signed and unsigned) are documented in Data types/Integral data types.

So the following compiles just fine...:


#COMPILE EXE
#DIM ALL

GLOBAL i AS INTEGER

SUB s()
GLOBAL j AS INTEGER
END SUB

FUNCTION PBMAIN () AS LONG
GLOBAL k AS INTEGER
END FUNCTION


Might be of interest that when using DIM, the compiler picks GLOBAL/LOCAL scope based on the declaration position:


#COMPILE EXE
#DIM ALL

DIM i AS INTEGER ' -- Will become GLOBAL variable, as DIM is used in GLOBAL space

SUB s()
DIM j AS INTEGER ' -- Will become LOCAL variable, as DIM is used inside SUB/FUNCTION/METHOD space
END SUB

FUNCTION PBMAIN () AS LONG
DIM k AS INTEGER ' -- Will become LOCAL variable, as DIM is used inside SUB/FUNCTION/METHOD space
GLOBAL k2 AS INTEGER ' -- Using GLOBAL forces the variable to be GLOBALy visible
END FUNCTION



Petr

José Roca
09-10-2011, 09:16
It will compile if you use the correct syntax



#Compile Exe
#Dim All

Global i As Integer

Sub s()
Global j As Integer
End Sub

Function PBMain () As Long
Global k As Integer
End Function



Int does not exist in the PowerBASIC language. Use Integer, if that is what the type you want, or Long, if its is a C++ Int.

danbaron
09-10-2011, 09:18
You're right Petr, I'm stupid.

And, the reason I thought the one case did compile was because, the "primary source file", was not the file I was viewing.

Dan

---------------------------------------------------------

So, then, the answer is yes, you can declare global variables from at least within subroutines, but, what good would doing that be?

José Roca
09-10-2011, 09:18
Petr has been quicker than I.

danbaron
09-10-2011, 09:47
If I recall correctly, in Turbo Pascal you would make a project consisting of multiple files.

Turbo Pascal always knew which files a particular project currently consisted of.

Each of the files you could make into a unit.

When you compiled the project (a menu command), only the units which had changed since the last compilation would be re-compiled.

You didn't need to "#include" anything.

You didn't need to "#compile" anything.

You didn't need to "#link" anything.

I agree that SLLs are good for "third-party" code.

-------------------------------------------------------------------------------------------------

Maybe that is why PowerBasic has always promoted how fast it compiles code, because, it would re-compile everything if even one character was changed.

Charles Pegge
09-10-2011, 11:36
Oxygen supports the nesting of true subroutines; functions within functions, to a depth of 7 levels. (I trust that no sensible program would require more than 2 levels. :) )

the static variables of the outer function are visible to its inner functions. But the local variables remain local to each function. I am not sure it would be desirable to make the outer locals directly visible to the inner function. And it is technically more tricky to do in a compiler.

A static variable in this context is one which is located in the global work space of the program but its visibility is restricted to the function / procedure in which it is defined.



function foo() as string


static string s="okay"


function boo() as string


function hoo() as string


static string t="hoo "


return t+" "+s


end function


return hoo


end function


return boo


end function


print foo



Charles

danbaron
09-10-2011, 13:34
It executes like lightning.

I saw the console program.

I saw a file program.

A lot of OOP.

I think I need to be able to allocate memory and to assign a pointer to it, i.e., dynamic arrays.

John Spikowski
09-10-2011, 16:32
So, then, the answer is yes, you can declare global variables from at least within subroutines, but, what good would doing that be?

Seems to defeat the purpose of using global variables if they have to be declared (initialized) in each routine. :shock28:

Charles Pegge
09-10-2011, 17:37
You need one function to assign initial values to the global variables. By default they are set to null or zero.

Charles Pegge
09-10-2011, 17:53
I think I need to be able to allocate memory and to assign a pointer to it, i.e., dynamic arrays.


This is one way of creating a dynamic array in Oxygen:

Supposing you wanted to create a dynamic array of vectors:



type vector single x,y,z


dim as vector byref v


@v=getmemory 1000*sizeof vector


v[100]<=10,11,12, 20,21,22


print v[101].y 'displays 21


freememory @v



It is possible to overlay an array onto any memory space, but you have to take care of bounds checking. Oxygen's arrays are primeval :)

danbaron
09-10-2011, 19:58
Nothing wrong with that at all.

I didn't see that way.