Jump to content

Photo

Global Variables vs. Global(Variables) & Functions - Maximums?

variables limits maximums caps global variable limit maximum cap local

  • Please log in to reply
46 replies to this topic

#31 RetraRoyale

RetraRoyale

    Doyen(ne)

  • Members

Posted 06 June 2014 - 05:26 PM

Functions are more complex than simple data types like ints and bools. A function is not identified by it's return type, but by it's whole type line (including the types of it's arguments.) This is why you'll see 'overloaded' functions all over the place. For example:

 

bool GetScreenDBit(int dmap, int screen, int d, int bit) {...}
bool GetScreenDBit(int screen, int d, int bit) {...}
bool GetScreenDBit(int d, int bit) {...}  

Here you have three functions, each with the same return type and name, but different argument types. Thus they are completely different functions. This is standard practice for almost all programming languages.

 

So there is no reasonable way to think of function declarations having anything to do with their return type, since the return type alone is not enough to declare a function. That's also why it seems to be a completely unreasonable jump to try and conclude bounds on return types are bounds on functions. Functions are bigger than their return types; they have to be bigger in order to work properly.

 

I'm not sure if or why boolean values don't autocast to 0/1, nor do I think it is important. The whole reason to use a boolean type is to avoid the dangers of that kind of thing. You can always write casting functions to recreate that functionality if you want. Internally, boolean values are usually just 0&1. Some languages optimize them to store multiple bools in a single byte, but I don't know if ZScript does.

 

I don't think it matters, since that is just low-level implementation detail. If the script you are writing needs to optimize every single bit from the start, then you are probably doomed before you begin. If every bit counts, then you drastically multiply your opportunity for error, and you have to build a mess of systems like 'bit-array managers' to do these optimizations. That's just more boilerplate waiting to fail or be made obsolete. Instead, you're supposed to trust the interface you are presented with -- that boolean constants occupy 1 unit of your available constant space.

 

That kind of internal detail is not necessary for user-level script writing. That's bad practice.


Edited by RetraRoyale, 06 June 2014 - 05:42 PM.


#32 Saffith

Saffith

    IPv7 user

  • Members

Posted 06 June 2014 - 05:47 PM

How close was I, on my comprehension of the .sav file format, earlier? I'm pretty sure that it has 256 registers (or something similar), each of limited field size, in which it can store the values. (I did not, and still do not, think that it has anything to do with RAM allocation; as an active value in memory shouldn't be a problem, and that only the .sav format is constraining everything. Feel free to correct me, if the way that ZC allocates memory maps in RAM is also convoluted.)

More or less. Global variables aren't on a global stack; they have distinct names in ZASM: gd1, gd2, gd3... Those are mapped to an array internally. In theory, increasing the limit is just a matter of resizing the array, adding more ZASM names, and updating the save format. Adding more names is the part I don't know how to do.
 

ZC does (for whatever reason) differentiate floats for some things, in the compiler, despite treating them in an identical manner internally. For example, a string array, is considered a float by the compiler, despite being declared, and used as an int. (This is most likely a reporting problem with the compiler itself.) Raw text is also interpreted as a float, when reporting errors.
 
I don't know why the compiler does this, but it does.

ints and floats aren't just treated identically internally, they're exactly the same thing. The compiler doesn't bother to remember which term you used; it calls all numbers floats.
 

I take it that booleans are treated as simple 0/1 ints internally? If so, why doesn't an int of 0/1 cross to a bool of false/true, when typecasting?

It doesn't? It should. Any nonzero number should be considered true.

#33 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 09 June 2014 - 06:39 AM

More or less. Global variables aren't on a global stack; they have distinct names in ZASM: gd1, gd2, gd3... Those are mapped to an array internally. In theory, increasing the limit is just a matter of resizing the array, adding more ZASM names, and updating the save format. Adding more names is the part I don't know how to do.
 
ints and floats aren't just treated identically internally, they're exactly the same thing. The compiler doesn't bother to remember which term you used; it calls all numbers floats.
 
It doesn't? It should. Any nonzero number should be considered true.

 

I'll verify that, when I have a chance. I seem to recall that a straight numeric value passed as an argument ,that expected a boolean value, reported a typecasting error; however my memory is far from infallible. It was probably the other way around (cannot cast from bool, to float), although I would think that if ZC treated bool false as 0, and bool true as 1, then it would be able to typecast those values directly in either direction. If bool true, is treated as a numeric value that is non-zero, but doesn't actually assign it a value of 1, or some fixed value, then that would cause this sort of problem.

 

(In other words, !0 isn't the same as 1, -1, or 2.034. It;s not a genuine number, so it can't be typecast. I also don't know how the compiler treats -0; if it's considered 0, if it's a non-zero number; or if -0 is distinct from 0, but using -0 as a value for bool true, could also provide some interesting results.)

 

I was always curious, why the compiler reports used the term 'float', when ZC doesn't truly handle floating point values. I know it's just a choice of terms, but I would have thought that in this case, the word int would be used, because that's what it really is.

 

 

Functions are more complex than simple data types like ints and bools. A function is not identified by it's return type, but by it's whole type line (including the types of it's arguments.) This is why you'll see 'overloaded' functions all over the place. For example:

 

bool GetScreenDBit(int dmap, int screen, int d, int bit) {...}
bool GetScreenDBit(int screen, int d, int bit) {...}
bool GetScreenDBit(int d, int bit) {...}

Here you have three functions, each with the same return type and name, but different argument types. Thus they are completely different functions. This is standard practice for almost all programming languages.

 

So there is no reasonable way to think of function declarations having anything to do with their return type, since the return type alone is not enough to declare a function. That's also why it seems to be a completely unreasonable jump to try and conclude bounds on return types are bounds on functions. Functions are bigger than their return types; they have to be bigger in order to work properly.

 

 

For clarity, many languages also specify that something as a function, when declaring it, as well as its return type. I'm used to seeing a function, subroutine, or other (external) programme, declared as such.

 

That's partly to blame for my mis-comprehension, regarding limits on functions designed to return as an integer, and integer variables, and my curiosity on what declared a function as a function.

 

In Fortran, we do this:

 

FUNCTION GENERATENUNBER(args)

 

That declares the function as such, and then, the function return type, is declared within it. Programmes are used to call to functions, and subroutines are used to manipulate input vars. Everything, is clearly distinctive, as it's declared by its type of operation.

 

Seeing this inverted, feels a bit off, because there's no direct 'function' declaration. Thus, when a compiler's only declaration types are int, float, bool, and void (plus the special, reserved types: item, global, and FFC); and you are told exactly this 'ZScript has a combined maximum of 256 ints, floats, and bools.', you may assume that this applies to any usage of those types. Clarity, of this sort, is intrinsically imperative; as an open-ended statement with room for ambiguity, can cause a good deal of confusion, and stress..

 

Thus, I asked to know exactly what was declaring a function, as discussed (and generally answered) earlier in the thread.

 

I did however, somehow forget that ZScript allows overloading of this type, a while back; and was reminded when re-reading ZScript.txt, and felt awfully stupid. Overloading is compiler-specific, after all.

 

It's my practice to name each function with something unique, to avoid the necessity of overloading; or to provide the args that it needs at one time. That's probably not optimal, but I do it nevertheless, often because I don;t make functions that needs to accept different input types, to perform identical operations. (I probably should consider that kind of use for some things.)


Edited by ZoriaRPG, 09 June 2014 - 07:21 AM.


#34 Master Maniac

Master Maniac

    Earth, Wind, Fire, and Water.

  • Members
  • Real Name:kris

Posted 09 June 2014 - 07:38 AM

Saffith, am I correct in assuming that using dummy functions in the place of global variables would completely circumvent the global variable limit, and instead affect the (much higher) scope-function limit? I mean the functions would have to be made and accessed differently because of this, but still.

 

In that case, what purpose does having a global variable limit really serve if it could be so easily worked around?



#35 RetraRoyale

RetraRoyale

    Doyen(ne)

  • Members

Posted 09 June 2014 - 12:53 PM

Saffith, am I correct in assuming that using dummy functions in the place of global variables would completely circumvent the global variable limit, and instead affect the (much higher) scope-function limit? I mean the functions would have to be made and accessed differently because of this, but still.

 

In that case, what purpose does having a global variable limit really serve if it could be so easily worked around?

 

Functions cannot be optimized in the same way that global variables can, so it is less efficient to use functions.

 

Also, you cannot assign to a function, so it is only useful for constants, which already are unlimited.

 

Unless I'm misunderstanding you...

 

 

 

 

In Fortran, we do this:
 
FUNCTION GENERATENUNBER(args)
 
That declares the function as such, and then, the function return type, is declared within it. Programmes are used to call to functions, and subroutines are used to manipulate input vars. Everything, is clearly distinctive, as it's declared by its type of operation.

 

 

 

Well, Fortran is pretty old. Mathematically (and according to Type Theory), if a type is denoted A, B, C, etc, then a function is denoted A->A, A->B, A->C, etc. A function accepts some input and outputs a single type. That's what 'function' means, so that's all the computer needs to do.

 

Modern language design is more likely to imitate the mathematical notation for functions. That is (mathematically):

 

f: X -> Y   "f is a function from domain X to codomain Y"

f = {...}     "f is defined by {...}"

 

translates to (In c-style code):

 

Y f(X) {...}   " f takes an X and outputs a Y according to the code {...} "

 

Saying that it's a function is really pretty redundant. (Note: X,Y can be tuples of multiple values if you wish. The idea of 'Currying' makes this distinction irrelevant.)


Edited by RetraRoyale, 09 June 2014 - 01:08 PM.


#36 anikom15

anikom15

    Dictator

  • Banned
  • Real Name:Westley
  • Location:California, United States

Posted 09 June 2014 - 12:55 PM

How can you circumvent the limit without static variables?

#37 LinktheMaster

LinktheMaster

    Hey Listen, Kid

  • Members
  • Real Name:Matt
  • Location:United States

Posted 09 June 2014 - 01:55 PM

If the limit on globals is really an issue (somehow), there are some things you could do to get around it.  One big thing I can think of is using the global function Game->GetScreenD() (at least I believe that's the name of it).  You could then create some Getter/Setter functions that will then take a numeric value, then evaluate that to figure out which map/screen/d value to use.  I know this works because Nick and I had to do this ages ago when global arrays didn't save with the quest.

 

Of course, you wouldn't really be able to have variables there, but you could technically create constants to use parameters for the function.  Plus, if you were going down this path, I'd just create a global array for this general thought process.  But, if for some reason if you've really used up all variables and can't use anymore arrays, then using Screen Ds is the next best option.  However, there's going to be some efficiency degradation if you do that a ton.

 

Zoria's idea, however, would not work.  If there were a limit on constants, then it would maybe be helpful.  But, that would still need global variables to save data to.



#38 anikom15

anikom15

    Dictator

  • Banned
  • Real Name:Westley
  • Location:California, United States

Posted 09 June 2014 - 02:35 PM

Functions cannot be optimized in the same way that global variables can, so it is less efficient to use functions.

Also, you cannot assign to a function, so it is only useful for constants, which already are unlimited.

Unless I'm misunderstanding you...




Well, Fortran is pretty old. Mathematically (and according to Type Theory), if a type is denoted A, B, C, etc, then a function is denoted A->A, A->B, A->C, etc. A function accepts some input and outputs a single type. That's what 'function' means, so that's all the computer needs to do.

Modern language design is more likely to imitate the mathematical notation for functions. That is (mathematically):

f: X -> Y "f is a function from domain X to codomain Y"
f = {...} "f is defined by {...}"

translates to (In c-style code):

Y f(X) {...} " f takes an X and outputs a Y according to the code {...} "

Saying that it's a function is really pretty redundant. (Note: X,Y can be tuples of multiple values if you wish. The idea of 'Currying' makes this distinction irrelevant.)


It doesn't have to do with Fortran being old, it's just a different paradigm. The modern dialect of Fortran came after C. There are more modern languages, like Ada, that work similar to Fortran.

That's where most of Zoria's problems are coming from. He's designing for a different paradigm.

#39 RetraRoyale

RetraRoyale

    Doyen(ne)

  • Members

Posted 09 June 2014 - 03:05 PM

It doesn't have to do with Fortran being old, it's just a different paradigm. The modern dialect of Fortran came after C. There are more modern languages, like Ada, that work similar to Fortran.

That's where most of Zoria's problems are coming from. He's designing for a different paradigm.

 

Yes, well it's an old paradigm, and has the problem of hiding important details about how type systems work. That's one of the reasons Zoria didn't know what a function was, how to declare one, or how to differentiate it from non-function types.



#40 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 10 June 2014 - 12:53 AM

Functions cannot be optimized in the same way that global variables can, so it is less efficient to use functions.
 
Also, you cannot assign to a function, so it is only useful for constants, which already are unlimited.
 
Unless I'm misunderstanding you...


I'm also not getting the intent here:

The singular point of global variables is that they store values accessible to anything else. You can get around that with arrays, but the larger the array, the more time it takes to read it. That's the painful trade-off.

Functions don't store anything, by nature. You cold probably return a value, and exchange it between two functions, perpetually, which would act like storing it, but that would be horribly limited, convolted, and potentially crippling.

Just an example: I'm not certain that this would work as predicted, and the third function isn't really necessary, but this is a model of how to do this. (You'd need to place these functions in a loop, to continually read their values, between the air, and then call them instead of a global variables, inside other scripts; because they are running as part of a global event.

The fourth function is to insert/change a value:


 
 

Well, Fortran is pretty old.


Only slightly older than me'self. This, and COBOL, are the languages that I leanrt at University: The main principals and syntax, are my basis for comprehension of al other languages. It's like a foundation layer, and my mind works in a way, that I interpret new styles, and new syntax, in a way that equates it to one of the two.

That example if for Fortran 77; it would be slightly different in Fortran 90, or 95+.
I could conjure up stuff for Fortran IV, through Fortran 77, in a trance state, but as you say, they aren't extremely common now. Most of what I did, was scientific formulation, and the systems that we were using, ran on Fortran and COBOL, because they were built on an extant system, that started therewith.

Outside of this, the other primary language that I used, was Perl; and that was primarily for interfacing with Unix System IV. I still used Perl on occasion, for BSD/Linux/etc., but not much in a long time.

I'm not certain on how different modern Fortran is, but it is still a current language, with reasonably regular updates.

Still, I think you get the point that I was making, on tokens.
 

How can you circumvent the limit without static variables?


No promises, but the method I mention above, might work. Because the value being exchanged is happening perpetually, it never leaves memory. This would function as a global variable while in operation, but you'd need to write it to an array, onExit.

You'd need a pair of functions, per value that you want to carry, and an array that can hold all of those values, to store them, for later retrieval.

Again, I say, this is neither optimal, nor fundamentally practical, but it may work, if you need to read, and write, to variables in memory.
 

int valueExchangeArray[1]={0};

global script onExit{
    void run(){
       valueExchangeArray[0] = read_exchangingValue();
    }
}

global script onContinue{
    void run(){
        insertValue(valueExchangeArray[0]);
    }
}

Zoria's idea, however, would not work. If there were a limit on constants, then it would maybe be helpful. But, that would still need global variables to save data to.


Which idea exactly? I'm starting to lose track here, as I posted a few (rather) different possible solutions.
 

...quest rules...


Regarding making storing global variables a quest rule, fr games where you need many variables that don't need to be stored, it may be more applicable to create another token type, tempvar that can be used for variables not to be saved.

In doing so, you don't need a quest rule at all. You just declare the token like this:
 

tempvar int ammunition = 10l;
tempvar float speed = 6.4;
tempvar bool alive = true;

That uses a syntax similar to what I described above (re: Fortran), by declaring the token, then the type. Any variable that has a tempvar token, would be kept in memory while the game is running, but not saved. If the programmer wants to save those values, he can store them to an array on exit, and load them on continue, avoiding array reading during a game.

That would offer a good deal of flexibility, in how to declare variables, without losing the benefit of either method. It would also probably be a nightmare to implement, but because you have a special token for it, it would be fully backward-compatible with older code.



#41 anikom15

anikom15

    Dictator

  • Banned
  • Real Name:Westley
  • Location:California, United States

Posted 10 June 2014 - 10:57 AM

That code will not compile. (Remember what I said about code samples?) I'm not going to guess what you're trying to do, but the idea that two functions can carry a variable will not work because functions cannot store variables unless they are static. (Everything is destroyed after the function is called.) A static variable is a variable that exists in memory for the entire life of the program. ZScript only supports these in the global scope (i.e. all global variables are implicitly static), not in function scopes.

I admit that I don't always test my code samples, but the difference between you and me is that I have enough experience to compile and test code in my head. For long examples, I'll test it. If it's a new language for me, I'll definitely test it so I know I'm doing it right and can learn from it. This is what you need to do.

tl;dr spend more time coding; spend less time writing posts.

Spoiler


P.S. Modern Fortran is Fortran 90+. The latest version supports object-oriented programming. It's hardly different from Fortran 77 (there's even a 'fixed' syntax mode where the old column rules are applied). It's a hardy language, and I like it.

Edited by anikom15, 10 June 2014 - 11:07 AM.


#42 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 10 June 2014 - 01:28 PM

That;s my fault: I typed it up quickly, as an example of a concept. It was just an idea: One that I've probem not to work.

 

The premise, is that a global script uses a function, to return a value to a second function, that returns it to the first, in perpetuity. Thus, while the original value is destroyed, because it's forwarded to a second function, it's temporarily stored there, then returned back to the first function, ans can be read by internalised code blocks/functions/scripts, inside the global script.

 

It's something I postulated as theory: I have no intent of trying to do something this convoluted, even if it was possible, because it doesn't serve any point. It was in direct response to master maniac asking how it could be done. extensive testing, or any testing, therefore, wasn't IMO, useful, as all I as doing here, was suggesting a potential method.

 

For your peace of mind, this is the set-up (that does, in fact, compile):


 

.

Alternate version with everything in the scope of the global active script:

 

This second method, was an experiment to see if it was possible to define a function within a global script, and to define an item script in the same scope. The former is fine but the latter is not. Local functions work, byt ZScript doesn't accept nested script tokens.

 

Now, while the former does compile, and could work, in theory, theory is as far as it goes. Because of the trading loop, even with a Waitframe, ZC loops in a manner that precludes any other actions, which is about what I anticipated from doing something like this. ( I recall that I said something about it crippling ZC, in my OP on the subject.)

 

The bottom line is, that ot may very well be shifting that value between the two functions, it can't do anything else (i.e. ZC crashes). There may be a way around that (although I suspect not), but that was the concept. Pass one value constantly between two functions so that it is never lost. One of the two functions would always be passing the value to the other, and both would always be retuning that value; therefore, floating it in memory.

 

In that context, while a function can never store a value, it can juggle it to another function, that does the same in reverse, preserving the value.

 

This is what happens when I don;t sleep for 26-hours, and I'm in a great deal of pain. I don't mind discussing any kind of theory or method, even if that discussion is purely academic, and otherwise impractical. When I know that it probably won;t be useful though, I don't give it my best, especially when i feel as if I've been run over by a lorry, and then trampled by horses.


Edited by ZoriaRPG, 10 June 2014 - 01:46 PM.


#43 anikom15

anikom15

    Dictator

  • Banned
  • Real Name:Westley
  • Location:California, United States

Posted 10 June 2014 - 01:46 PM

Hmm, you say you compiled it, but did you test it (with a value other than 0)? It seems like it would not work. I'd test it myself but I'm at work. :P I'll give it a proper when I get home.

Anyway, theoretically your method should not work because functions do not store variables nor do they remember what values were passed to them in the past. Basically you're supposing that values can be suspended in 'air' like two people throwing a ball back and forth. There is, in fact, no 'air' for the values to exist in.

#44 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 10 June 2014 - 02:14 PM

Hmm, you say you compiled it, but did you test it (with a value other than 0)? It seems like it would not work. I'd test it myself but I'm at work. :P I'll give it a proper when I get home.

Anyway, theoretically your method should not work because functions do not store variables nor do they remember what values were passed to them in the past. Basically you're supposing that values can be suspended in 'air' like two people throwing a ball back and forth. There is, in fact, no 'air' for the values to exist in.

 

I did test it, and it does not work. (I said that above.)

 

What I was expecting may have been possible, is similar to what you described. I would think that if the same value is perpetually passed between two functions, that when those functions do a return, the return value can be used. that;s why i not only had then passing the value to one-another, but also returning it so that it could be used, and updating a local var when that happens; but I stress, it doesn't work, and I've abandoned the model.

 

It may very well be passing, returning, and storing the values: However, in the process, ZC hangs, because it can't do anything other than those actions.

 

If you want to play with it, that;s fine,. but I would't waste any further time on it, unless you see some valid reason to do thus, I did it, just to see what the result would be, not because it has any useful purpose. It was literally nothing more than an attempt to see if it was at all possible.

 

Please read post 34 by master maniac, if you're curious as to what inspired me to try this; and my response in post 40:

 

'Functions don't store anything, by nature. You cold probably return a value, and exchange it between two functions, perpetually, which would act like storing it, but that would be horribly limited, convoluted, and potentially crippling.'

 

I admitted, before posting the model, that it probably wouldn't work--or if it did, it would break ZC--but I figured I'd try it, to be certain.

 

You're essentially re-enforcing my points above; but rather than discounting it out of hand, I tried to conceive of a method that might do this. It doesn't bother me that it didn't, and there may be a way around the problem, but it;s a terribly hack-ish, and fruitless exercise.

 

If you;re curious, the reported error (tracing) is: Stack over or underflow, stack pointer = 0

(Assigning value as non-zero doesn't change that, if you're wondering.)

 

In the end, I;d rather try something new, and find that it;s not possible, or fail in the attempt, than not to try at all, at all.

---

 

More useful: What do you think of my concept, regarding a token type for vars not to be saved?


Edited by ZoriaRPG, 10 June 2014 - 02:26 PM.


#45 anikom15

anikom15

    Dictator

  • Banned
  • Real Name:Westley
  • Location:California, United States

Posted 10 June 2014 - 02:55 PM

The problem is the functions are mutually recursive (the two functions constantly call each other, resulting in an infinite number of calls; none of which ever return). The program should crash eventually because the function stack will overflow.

I'll still analyze your code later since it is just too good of a teaching opportunity to pass up. You should know why your code doesn't work anyway.

As for your tempvar syntax, no. Not only does it consume a new keyword (tempvar is a name that could very likely be used by a programmer either in the past or future), but it is also bad design (IMO) to have any variables save state at all. Here are five of my own ideas (starting from ideal to practical):

1. All variables are reset whenever the quest begins. To store values, built-in save and load functions allow various objects to be saved. The global script would typically call load before the main loop. (This is how com. games tend to do it.) The problem with this is backwards incompatibility. The benefit is any object can be saved, not just globals.

2. All variables reset. Make a new keyword, static, that forces a global to be saved. In the context of ZScript, static on a global in the C sense would be useless (I think/hope). static is unlikely to have been used as a variable name (cuz C and it's an adjective). The drawbacks are backwards incompatibility and confusion with C and static variables in functions (if implemented).

3. 2 but with keyword volatile, extern, register ... all C reserved words whose definition kind of applies to the above. Same drawbacks.

4. Exactly the same as your suggestion, but use auto for automatic variable. (It automatically resets!) Drawback is confusion with C++11's definition.

5. Replace ZScript with Python ... or Ruby ... or anything really :D

Edited by anikom15, 10 June 2014 - 03:04 PM.




Also tagged with one or more of these keywords: variables, limits, maximums, caps, global, variable, limit, maximum, cap, local

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users