Page 1 of 2

Variable scoping - Local variables inside loops and ifs, etc

Posted: Thu Mar 01, 2007 1:09 am
by Joakim Christiansen
I want to be able to define local variables inside loops and ifs, etc. When you define variables inside IF's or loops or whatever I want them to be protected inside there and deleted afterward. I don't know which keyword that would be best to use for this, maybe Define, Protected or something new like Local?

Like this:

Code: Select all

If DoSomeAdvancedStuffThatRequiresManyVariablesYouDontWantToBeGlobal
  Local.l Variable1, Variable2=20 ;only available inside the if
  
  For Variable1=0 To Variable2
    Local.l Test1, Test2=20 ;only available inside the for-loop
    Print(Variable1)
    ;bla bla bla
  Next
  
EndIf
The reason I want this is because you can do stuff like this in Visual Basic and it makes the code so much cleaner and smarter! I would really love this feature!

Posted: Thu Mar 01, 2007 10:51 am
by GedB
Java has this feature, and in my experience it is more trouble than its worth.

The problem is the overhead of constantly allocating and deallocating the variables.

The result is that most programmers are careful to define their variable outside the loop.

Also, the example you give leads to headaches. Can you spot what is wrong with the following:

Code: Select all

If DoSomeAdvancedStuffThatRequiresManyVariablesYouDontWantToBeGlobal
  Local.l Test1, Test2=20 ;only available inside the if
  
  For Test1=0 To Test2
    Local.l TestI, Test2=20 ;only available inside the for-loop
    Print(Test1)
    ;bla bla bla
  Next
 
EndIf
If you want scope, create a Procedure. Simple.

Posted: Thu Mar 01, 2007 7:06 pm
by Character
It prints only one zero or it prints 21 zero's? I'm not sure.

(If TestI and Test1 are the same... (typo?))

Posted: Thu Mar 01, 2007 7:14 pm
by dracflamloc
The overhead isn't really worth it. All you need to do really is zero out your variables at the start of the next loop, etc.

Posted: Mon Apr 02, 2007 1:24 pm
by Joakim Christiansen
dracflamloc wrote:The overhead isn't really worth it.
But if I put code like this into a procedure instead, doesn't that create some overhead too?
I think it would be nice to be able to program like this, and if you are scared because of any overhead then just don't use this feature.

Re: Variable scoping - Local variables inside loops and ifs,

Posted: Wed Apr 04, 2007 4:22 pm
by Rescator
Joakim Christiansen wrote:

Code: Select all

If DoSomeAdvancedStuffThatRequiresManyVariablesYouDontWantToBeGlobal
  Local.l Variable1=0, Variable2=20 ;only available inside the if
  
  For Variable1=0 To Variable2
    Local.l Test1=0, Test2=20 ;only available inside the for-loop
    Print(Variable1)
    ;bla bla bla
  Next
  
EndIf
If you really must do something similar do this instead:

Code: Select all

  Define.l Variable1, Variable2=20
If DoSomeAdvancedStuffThatRequiresManyVariablesYouDontWantToBeGlobal
  
  Define.l Test1=0, Test2=20 ;only available inside the for-loop
  For Variable1=0 To Variable2
    Print(Variable1)
    ;bla bla bla
  Next
  
EndIf
PureBasic should have no issues with this! And it seems to behave like you want it too. The benefit is that it's outside the loop so it's way faster and you avoid endless loop bugs that you'd get. (as pointed out earlier)

PPS! Make sure you use Define.l Var=0
if you just use Define.l Var it will not be set to 0 but previous value kept.

PPPS! Define do not make them global, they do however stick around in the "main" code, so reusing them may be smart. (saves a few bytes here and there) PB do not have a main() as C tend to do, PB's main is well, anything in the main code body. (aka not in procedures)

Posted: Wed Apr 04, 2007 4:56 pm
by Kaeru Gaman
what would that mean...
should I declare a dozen variables shared if I want to access them within the loop?

I already read such a request somewhere else earlier, and I see no advantage in it...

like GedB said: it is more trouble than its worth.

Posted: Wed Apr 04, 2007 5:21 pm
by Rescator
Yup! I think the current way works great, if you need temporary variables this is the better way to do it.

It even works with enableexplicit :)

Code: Select all

EnableExplicit

Procedure test()
  Protected Test1.l=0, Test2.l=20
  For Test1=0 To Test2
    ;Debug Variable1
  Next
EndProcedure

Define.l Test1=0, Test2=20
For Test1=0 To Test2
 ;Debug Variable1
Next
 

Debug Test2

test()

Define.l Test1=0, Test2=10
For Test1=0 To Test2
 ;Debug Variable1
Next

Debug Test2

Personally I tend to call variables like these a,b,c or n,i and similar.
Then again in the main code I always define those only once as well.
And I almost stopped fully using global variables,
now I use a structure (or several) and a single global variable. (call me weird) PS! Is that more stack friendly? (looks the at PB gurus)

Posted: Wed Apr 04, 2007 5:23 pm
by ts-soft
A Userdefined Scope for Variables and Procedures is a nice idea, but not in this way

Posted: Wed Apr 04, 2007 7:25 pm
by Demivec
ts-soft wrote:A Userdefined Scope for Variables and Procedures is a nice idea, but not in this way
Maybe something like this:

Code: Select all

Define.l Variable1=0,Test1=4,Test2=2
;blah blah
BeginDefine.s Test1l="hello"
  If Variable1=Test2
    BeginDefine.l Test2=test()
      If Test2>Len(Test1)
        Debug Variable1
      Endif
    EndDefine Test2
  Endif
EndDefine Test1
You really only need to mark the boundaries of when something is defined or not, in a procedure you might use an UnDefine command. The only use I can see in doing this is for giving "single use" variables meaningful names, instead of using them over and over for different things and giving them a generic name. An example would be:

Code: Select all

Result.l=test(5)
Debug Result

Result.l=windowID(0)
Debug Result

;or alternatively
BeginDefine.l Checksum=test(5)
  Debug Checksum
EndDefine Checksum

BeginDefine.l LastWindowID=windowID(0)
  Debug LastWindowID
EndDefine LastWindowID
A command to "alias" the variable would also be a solution to this scenario.

Posted: Wed Apr 04, 2007 7:37 pm
by ts-soft
My idea is like this:

Code: Select all

Global Hello.l

Procedure Foo()
  ; some code
EndProcedure

; ##############
BeginScope
Global Hello2.l ; only in this Scope global

Procedure Foo2() ; only in this Scopa 	available
  ; some code
EndProcedure

EndScope
; ##################################


; Hello global, Foo() avalaible
; Hello2 not defined, Foo2() not available


Posted: Thu Apr 05, 2007 6:21 pm
by Demivec
@ts-soft:

Your idea seems like it would share somethings in common with a VB6 Module like feature.

Posted: Thu Apr 05, 2007 6:32 pm
by ts-soft
Demivec wrote:@ts-soft:

Your idea seems like it would share somethings in common with a VB6 Module like feature.
Yes :wink:
Is good for oop with priv. Procedures as Methods and so on

Posted: Fri Apr 06, 2007 8:08 pm
by Dr. Dri
Maybe a Block/EndBlock keyword ?

Dri

Posted: Sat Apr 07, 2007 2:51 am
by Dummy
Demivec wrote:A command to "alias" the variable would also be a solution to this scenario.
There's already a solution for your "alias":

Code: Select all

Macro Line
  Command
EndMacro

Procedure DoSomethingImportant()
  Protected Command.s

  For i = 0 to count ; loop 1
    Command = ProgramParameter()
    ; ...
  Next

  For i = 0 to 10 ; loop 2
    Line = ReadString(1)
    ; ...
  Next
EndProcedure
It's ugly, I know...

In this example the variable "Command.s" from loop 1 is reused as "Line.s" in loop 2.

Now there are two questions:

1. Why do I reuse that variable instead of declaring another one?
Imagine a Procedure that is called more often, maybe even recursive, and imagine there'd be 10 or 20 such loops - it would lead to a lot of memory beeing wasted or in the worst case even to a stack overflow.

2. Why don't I simply call that Variable String1?
Because I want my code to be readable!

PS and BTW:
A nice solution would be:

Code: Select all

Procedure DoSomethingImportant()
  Protected Command.s Alias Line.s Alias String.s Alias ...

  For i = 0 to count ; loop 1
    Command = ProgramParameter()
    ; ...
  Next

  For i = 0 to 10 ; loop 2
    Line = ReadString(1)
    ; ...
  Next

  For i = 0 to 10000 ; loop 3
    String = Mid(...)
    ; ...
  Next
EndProcedure
or:

Code: Select all

Procedure DoSomethingImportant()
  Protected String1

  Alias String1 As Command
  For i = 0 to count ; loop 1
    Command = ProgramParameter()
    ; ...
  Next

  Alias String1 As Line
  For i = 0 to 10 ; loop 2
    Line = ReadString(1)
    ; ...
  Next

  Alias String1 As String
  For i = 0 to 10000 ; loop 3
    String = Mid(...)
    ; ...
  Next
EndProcedure
or maybe even this:

Code: Select all

Procedure DoSomethingImportant()
  Protected String1

  Alias String1 As Command
  For i = 0 to count ; loop 1
    Command = ProgramParameter()
    ; ...
  Next
  RemoveAlias Command

  Alias String1 As Line
  For i = 0 to 10 ; loop 2
    Line = ReadString(1)
    ; ...
  Next
  RemoveAlias Line

  Alias String1 As String
  For i = 0 to 10000 ; loop 3
    String = Mid(...)
    ; ...
  Next
  RemoveAlias String
EndProcedure