Log In  

SOLVED: See @samhocevar's solution. Thanks everyone who contributed answers !

It's a pretty simple question that may not have so simple an answer.

Is there a way I can declare locally global variables inside a function.

By this I mean I define variables inside a function that meet two states.

  1. The variable is seen for the first time and is therefore initialized with a new value.

  2. The variable has been seen before and is NOT initialized but in re-entering this function it retains the same values it had before this same function was exited earlier.

I know I can simply declare global variables but what I wanted was to be able to retain values of simply named variables that might be just a single letter. Yet can only be seen and globally recognized by that local function.

Here is an example:

function _init()

word={"one","two","three"}

for i=1,#word do
  count(word[i])
end

end

function count(a)
localglobal n=0
  n+=1
  print(n.." "..a)
end

The results would be:

1 one
2 two
3 three

Understanding that the variable N inside the function of count() will have an entirely different value or no definition at all outside the function where it was not called or initialized.

Can this be done - or is there a kludge ?

P#71597 2019-12-31 18:53 ( Edited 2020-01-02 17:06)

Well, not exactly but sort of.

You can define global variables inside of a function just fine, but they will still be global not just local to the function.

So something like this:

function tiny()
 a = "hello world"
end

--print(a)  would yield [nil] right now because 'a' hasn't been defined yet

tiny()

print(a)

Would print "hello world"

But that's just defining and using a normal global, it will be the same everywhere, inside and out of the function.

The only way to achieve what you're suggesting is to either return the value(s) you want to retain or nest the function.

Something like this if you use nesting in functions:

function _init()
 word={"one","two","three"}
 count(word)
end

function count(a,n)
 if(n==nil) n=0
 n+=1
 print(n.." "..a[n])
 if(n<#a) count(a,n)
end
P#71600 2019-12-31 21:00 ( Edited 2019-12-31 21:03)

the thing for global and local is when ur considering using inheritance:
cuz then u need to consider which is private and which is public... in terms of the data.
thats what i heard anyway...

P#71606 2019-12-31 22:18
:: dw817

Trying your code out, @Cabledragon. Yeah, it repeats the 1,2 and 3.
Here is the test:

function _init()
 word={"one","two","three"}
 word2={"four","five","six"}
 ?n
 count(word)
 count(word2)
 ?n
end

function count(a,n)
 if(n==nil) n=0
 n+=1
 print(n.." "..a[n])
 if(n<#a) count(a,n)
end

The results I would want are:

[NIL]
1 ONE
2 TWO
3 THREE
4 FOUR
5 FIVE
6 SIX
[NIL]

Hmm ... So maybe I came across a programming idea that has yet to be invented. I can definitely see it being useful in a lot of functions.

For instance, that last cart I wrote, Map Compressor:
https://www.lexaloffle.com/bbs/?tid=36426

It relies on true global variables that I wish were only local to the function I'm calling it from. Or you could even use a command like SHARE(function-name) where any Global-Locals would be copied to any other function with a different name.

Just an idea - and I don't suppose there is any way to do it at all in any programming language ...

P#71607 2019-12-31 22:21 ( Edited 2019-12-31 23:07)
:: merwok
1

I think you’re looking for a closure! Variables defined inside a function (a) that defines another function (b) can be seen from b:

function a()
  local x = 42

   function b()
     print(x)
   end

   return b
end

b = a()
b()

You can play with that to see if b can write to x, I haven’t checked!
If we are lucky and Lua behaves like other languages I know, each call to a creates an independent scope or namespace with its own version of x and its own version of b that accesses or writes to x.

Another search keyword for this is «free variables».

P#71608 2019-12-31 22:29 ( Edited 2019-12-31 22:33)
:: dw817

Hi @chizel9000, I don't think there's any inheritance needed.

Well, the last bit I wrote (complex) where you share two sets of globals.

But that's not entirely necessary. I'd be content just to have single globals for single functions.

P#71612 2019-12-31 23:06 ( Edited 2019-12-31 23:07)
:: dw817

@merwok, this is interesting what you did here. I knew at a level you could have parent and children functions but I didn't know what their scope of access was.

So any function inside another treats the outer variables as global. This may solve my little dilemma. It doesn't give out a true Global-Local as I defined, but it comes close.

And yes, I'll look up the term, "free variables."

Thanks BIG !

P#71613 2019-12-31 23:11 ( Edited 2020-01-01 03:30)
2

A closure is the most elegant way to achieve this, but you can also restrict the variable to a specific do/end scope that also contains the function:

do
  local n=0
  function count(a)
    n+=1
    print(n.." "..a)
  end
end
P#71626 2020-01-01 13:16

FWIW I think what dw817 is trying to describe could be accomplished with a local static variable in some languages. It'd be only accessible within the scope that it is declared within, but retain its value between calls. Not a novel concept by any means.

E: cooked up a quick C++ example:

// code modified from https://www.geeksforgeeks.org/static-keyword-cpp/
#include <iostream> 
#include <string> 
using namespace std; 

void demo() 
{ 
    // static variable, initialized when first encountered
    static int count = 0; 
    cout << count << " "; 

    // value will retained for subsequent calls to this function
    count++; 
} 

int main() 
{ 
    demo(); // 0
    demo(); // 1
    cout << "blah ";
    // cout << count << " ";  // will not work
    demo(); // 2
    demo(); // 3
    return 0; 
} 

Output:

0 1 blah 2 3

I'm not super familiar with lua or Pico-8's version thereof but as far as I can tell it doesn't support static vars, or at least not in such a simple and straightforward way.

P#71628 2020-01-01 13:59 ( Edited 2020-01-01 14:32)

Kludge option: maybe run it as a coroutine?

function _init()

word={"one","two","three"}
c_count = cocreate(count)

for i=1,#word do
  coresume(c_count,word[i])
end

end

function count(a)

local n=0
while true do
  n+=1
  print(n.." "..a)
  a = yield()
end

end

(edit: error message is from when I had "c" instead of "c_count" in the coresume() command - the program as included above produces the desired output.)

Instead of running the function anew each time, this runs only part of the function each time and then pauses until the program picks it up again ... so whatever local variables are defined in the function stay defined within the function, because the function doesn't end.

P#71629 2020-01-01 15:06 ( Edited 2020-01-01 15:08)
:: dw817

My ! So many good answers, let me try the next idea. The DO with the function after by @samhocevar.

... Good answer !

Here is the test:

do local a=1
function addit()
  ?a
  a+=1
end end

cls()
a=5
for i=1,3 do
  addit()
end
a=5
for i=1,3 do
  addit()
end

Results are 1,2,3,4,5,6.

I want the numbers to be 1-6 - and they are ! Apparently there's more than one way to approach this Global/Local method. Nicely done !

P#71666 2020-01-02 02:50 ( Edited 2020-01-02 02:57)
:: dw817

@cakepie, think I can get you to convert that to Pico-8 to see it works ?

@packbat, I believe your method works too but so far I'm going with the simplest solution of two commands, DO/END to declare a global/local variable.

It's such an easy answer, I hope this helps some other people out there who also needed a unique variable of this type.

So many different answers - so many ways to approach it. I'm now wondering if this is something only Pico-8 can do or perhaps other graphic intensive languages like Blitzmax ... but I'll cross that bridge if I need to later.

@merwok, you are not forgotten. While your answer is directly what I need for my current code, a function within a function, the DO/END is also going to be quite helpful for future carts.

Thanks for all your help guys ! :D

P#71669 2020-01-02 02:56 ( Edited 2020-01-02 03:01)

I was simply interjecting to point out that the idea of "static local variables" exists in multiple other languages where it can be simply applied with its own keyword, even if lua doesn't support doing it the same way.

Since the concept has a name it's better to use established terminology rather than something you coined by yourself -- "global local" is self-contradictory and confusing: strictly speaking "global" vs "local" refer to scope, whereas "static" refers to lifetime/extent -- see here for elaboration. In lua global variables are always static; but the part of it you desired is the "static" lifetime and not the "global" aspect since you actually want local scope.

(As an aside, you may have also noticed a couple of people refer to "closure", which is a technique that is suggested as it can be leveraged to do what you want.)

Hope that's more informative/educational than my earlier post. In any case re: your request, others more fluent in lua / pico-8 have already shown possible approaches to obtain the behavior you asked for and I have nothing to add in that regard.

P#71672 2020-01-02 04:51

If you wanted it to function like that, you would have to use a closure as is recommended from others.
Also you have to be careful, because what you've asked me to do is count a variable "n" up to 6 but access two separate arrays who each have a maximum index of 3. So when we hit n=4 here, obviously "word2[4]" is not going to give us the result we desire as it does not exist!

For this reason, the "print()" line in the "count() function now specifies a[i] instead of a[n] so it does not go out of bounds.

function _init()
function _init()
 word={"one","two","three"}
 word2={"four","five","six"}
 ?n
 do
  local n = 0
  n = count(word,n)
  count(word2,n)
 end
 ?n
end

function count(a,n)
 if(n==nil) n=0
 for i=1,#a do
  n+=1
  print(n.." "..a[i])
 end
 return n
end
P#71695 2020-01-02 16:46
:: dw817

Oh it's fine Cable, thanks ... Here let me add SOLVED for the title. The concept of DO/LOCAL/END from @samhocevar did indeed solve it quite nicely. "Free Variables" if you want to call them that. "Static Locals" may be more accurate.

@merwok's solution for a double function is also quite useful and directly what I needed for my last cart.

THANK YOU everyone who contributed answers ! This was indeed quite the brainstorming party. :)

P#71697 2020-01-02 17:05 ( Edited 2020-01-02 17:15)

[Please log in to post a comment]

Follow Lexaloffle:        
Generated 2020-04-05 20:29 | 0.142s | 2097k | Q:47