Variable scoping - Local variables inside loops and ifs, etc

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Variable scoping - Local variables inside loops and ifs, etc

Post 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!
I like logic, hence I dislike humans but love computers.
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post 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.
Character
Enthusiast
Enthusiast
Posts: 337
Joined: Mon Aug 07, 2006 3:51 pm
Location: Netherlands

Post 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?))
Cessante causa cessat effectus
dracflamloc
Addict
Addict
Posts: 1648
Joined: Mon Sep 20, 2004 3:52 pm
Contact:

Post 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.
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Post 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.
I like logic, hence I dislike humans but love computers.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

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

Post 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)
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Post 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.
oh... and have a nice day.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post 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)
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Post by ts-soft »

A Userdefined Scope for Variables and Procedures is a nice idea, but not in this way
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Post 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.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Post 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

PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Post by Demivec »

@ts-soft:

Your idea seems like it would share somethings in common with a VB6 Module like feature.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Post 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
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
Dr. Dri
Enthusiast
Enthusiast
Posts: 243
Joined: Sat Aug 23, 2003 6:45 pm

Post by Dr. Dri »

Maybe a Block/EndBlock keyword ?

Dri
Dummy
Enthusiast
Enthusiast
Posts: 162
Joined: Wed Jun 09, 2004 11:10 am
Location: Germany
Contact:

Post 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
Post Reply