|
12
|
Static variables, I've been having a lot of trouble with this too.
In psuedo/jack-ish code
Main
{
MyClass z,y;
// So z.MyStatic should equal 4500 but I want y.MyStatic = 5500;
}
class My Class
{
static int MyStatic
method new()
{
let MyStatic = 4500; // Initialise MyStatic but only once
return;
}
method SomeOtherMethod()
{
let MyStatic = MyStatic + 1000;
return;
}
I have tried a lot of things, using getters and setters to access them and all kinds of other stuff but either MyStatic is 4500 or I get an error.
I'm expecting (and perhaps this is the trouble) 'static' to be like the C family of languages version of static, which is slightly implied by figure 9.7.
(for example... https://www.geeksforgeeks.org/static-variables-in-c/)
Is it possible to assign a starting value to a static just once and then each subsequent object increments static and then the next object gets the incremented value..And how would I implement that ?
Thanks yet again, hopefully that should be it for a while (famous last words
|
Administrator
|
Lozminda wrote
Static variables, I've been having a lot of trouble with this too.
In psuedo/jack-ish code
Main
{
MyClass z,y;
// So z.MyStatic should equal 4500 but I want y.MyStatic = 5500;
}
Then you want z and y to have DIFFERENT variables called MyStatic, which means you want them to be field variables and not static variables. Static variables are a single instance of a variable visible to all functions and methods of a class.
<quote> class My Class
{
static int MyStatic
method new()
{
let MyStatic = 4500; // Initialise MyStatic but only once
return;
}
method SomeOtherMethod()
{
let MyStatic = MyStatic + 1000;
return;
}
This shouldn't be a problem, but after invoking SomeOtherMethod, MyStatic will be 1000 larger for EVERY user of that variable.
|
|
This post was updated on .
Then you want z and y to have DIFFERENT variables called MyStatic, which means you want them to be field variables and not static variables. Static variables are a single instance of a variable visible to all functions and methods of a class.
No, I want MyStatic to be shared by z and y...
This shouldn't be a problem, but after invoking SomeOtherMethod, MyStatic will be 1000 larger for EVERY user of that variable.
I don't know what you mean by EVERY user of that variable. "user" you mean object ?
I think what your saying is that everytime an object of MyClass calls SomeOtherMethod MyStatic will be 1000 larger. Yes, that's what I want.
I have managed to get something to work, it is extremely ugly an I can't believe that what I've coded is the correct solution. It involves three other variables, passing a variable to the constructor and using a getter (ugg). I can post the code if you'd like, however:
Here's a quick example in C
int fun()
{
static int count = 0;
count++;
return count;
}
int main()
{
printf("%d ", fun());
printf("%d ", fun());
printf("%d ", fun());
printf("%d ", fun());
return 0;
}
Ouput 1,2,3,4
Everytime fun is called count goes up by one, static int count = 0 is only 'read' once. (I know it's not in a class, it's the behaviour of count that i'm looking for. Initialise once, increment on subsequent calls ignoring static int count = 0
I was hoping you might post an example of how to implement the above behaviour just to save me a little time please or point me to a better example than the book (or any of the jack progs that come with it for that matter)?
I can post what I've done, but it'll take half an hour to edit into something sensible, then there'll be some back and forth no doubt, and I've already taken up a lot of our time (personally getting to this point (including our previous chat re class/method/libraries) a good days work, 8-10hrs) and basically I'm running out of time very unfortunately. The real world is interfering with my learning !
I'm running out of languages to say thank you in, Merci !
PS I can't find any examples of the behaviour I want in Jack, believe me I've looked. Many examples use the static keyword, but they seem to be using like the C const, rather than static.
PPs I appreciate the C example is not the best, but in the light of our previous conversation hopefully that makes sense, the shared static incrementing when any object of the same class increments it. The problem is the initialisation only once
|
Administrator
|
Lozminda wrote
Then you want z and y to have DIFFERENT variables called MyStatic, which means you want them to be field variables and not static variables. Static variables are a single instance of a variable visible to all functions and methods of a class.
No, I want MyStatic to be shared by z and y...
Then I don't know what you meant by
MyClass z,y;
// So z.MyStatic should equal 4500 but I want y.MyStatic = 5500;
If MyStatic is SHARED by z and y, how can z.MyStatic equal 4500 while y.MyStatic equal 5500?
|
|
Because a method has altered it and the new value is now something different.
jack Static is not what C programmers would call const is it ?
|
|
Maybe this is better...
Static variables are a single instance of a variable visible to all functions and methods of a class.
How do I code that so that the static is initiated once and assigned a value (when the first object is created) but then Isn't re-initiated on the creation of subsequent objects?
|
Administrator
|
Lozminda wrote
Because a method has altered it and the new value is now something different.
So what do you expect z.MyStatic to now be?
jack Static is not what C programmers would call const is it ?
It's pretty much, but not exactly, what a static variable in C is. Keep in mind that what 'static' means in C is somewhat dependent on where in the program that keyword is used.
But for cases where you define a local variable in a function to be static, then that variable is "statically allocated" (hence the name) meaning that it's lifetime lasts from the beginning of the program to the end of the program.
It is NOT a const (unless you add that modifier) and you can change it all day long. But it's value is retained from one invocation of a function to the next.
|
|
Great we agree at this point...
|
|
What I should have said (had to leave and was being hastey)
Is that I agree with your C definition of static.
From what I can work out jack.static is different from C_Family.static. I'm going to have another go at explaining what I'm trying to understand later/express in Jack, in simpler terms, as I haven't got an answer to my question. Unfortunately I don't have time now, it'll have to be later. Thanks for your efforts, hopefully we'll get it hammered out during the week/next weekend.
Also getting this definition/utilisation sorted will help me with the later projects (i'd imagine) and Might help others with a C/C++ background too (Maybe).
If anyone else happens to be reading this and understands the question that I'm asking maybe a third point of view would be helpful.
Thanks WBahn for you time and efforts, an anyone else that wants to contribute !
Lozminda
Ps I signed off but then thought I'll look for a quick example online in C++
class Box {
public:
static int objectCount;
// Constructor definition
Box(double l = 2.0, double b = 2.0, double h = 2.0) {
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// Increase every time object is created
objectCount++;
}
double Volume() {
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
// Initialize static member of class Box
int Box::objectCount = 0;
int main(void) {
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
}
Box.objectCount increments everytime an object of type Box is created. Is it possible to do this in Jack, and could i have a code example in Jack of how to do it please. I don't think I can express myself any clearer, but I've got a week if not.
All the best
|
Administrator
|
Lozminda wrote
Maybe this is better...
Static variables are a single instance of a variable visible to all functions and methods of a class.
How do I code that so that the static is initiated once and assigned a value (when the first object is created) but then Isn't re-initiated on the creation of subsequent objects?
I would do this the same way that several of the o/s libraries are initialized. Create an init() function that is called at the beginning of the program (the person writing the program has to remember to do is) that sets the value of the static variables.
If you wanted to avoid this and make it invisible to the user, you could create an additional static variable that your constructor checks looking for a specific sentinel value, such as 4242. So your init() code might look like:
function void init()
{
if (initFlag = 4242)
{
return;
}
myStatic = 1000;
...
}
and your constructor might look like
constructor MyClass new(...)
{
do MyClass.init();
...
}
Since the initial values of static variables is unknown (IIRC), you are relying on it being very unlikely that the initFlag variable will just happen to be equal to 4242 by random change during the power on process (you have about a 1 in 65,000 chance of being unlucky enough for this assumption to be wrong).
|
|
Hi
Apologies for the delay, paid work got in the way ! I've managed too code a slightly less ugly (but still ugly) implementation of the static behaviour i'm trying 'to get'
constructor MyClass new(String inputString, bool IsThisTheFirst_TimeAnObect_of_thisClassHasBeenConstructed )
{
if (IsThisTheFirst_TimeAnObect_of_thisClassHasBeenConstructed ) let MyStatic = StartingValue;
...
}
And in the main function
{
var MyClass a, b, c;
let a = MyClass.new("SomeInput", true); // It works, but not ideal
let b = MyClass.new("SomeInput", false);
let c = MyClass.new("SomeInput", false);
}
It was partially inspired by your reply (WBahn) but I couldn't quite understand what your code was saying....
(I appreciate that you don't want to hand the answer on a plate in true N2T style..)
function void init() // This clearly isn't a method in MyClass. It will almost always return myStatic as 1000.
{ // but as it's not a method in MyClass it Doesn't have access to myStatic. Ok so a getter
if (initFlag = 4242) // and setter is used, but MyClass has to be initialised with var, then call void init, then
{ // call the constructor ?
return; }
myStatic = 1000; // This should be ??: let myStatic = MyClass.set_myStatic(1000);
...
}
and your constructor might look like
constructor MyClass new(...)
{
do MyClass.init();
...
}
So here is my jack version based on your suggestion..
MyClass.jack:
class MyClass
{
static int MyStatic;
field int MyInt;
constructor MyClass new()
{
let MyInt = 4;
let MyStatic = MyStatic + 1000;
return this;
}
method int getMyStatic() { return MyStatic;}
method int setMyStatic(int value)
{
let MyStatic = value;
return MyStatic;
}
}
Init.jack
class Init
{
function void init()
{
var int initFlag;
if (initFlag = 4242)
{
return;
}
let MyClass.MyStatic = 1000;
return;
}
}
Main.jack
class Main
{
function void main()
{
var MyClass x,y,z;
do Init.init();
let x = MyClass.new();
let y = MyClass.new();
let z = MyClass.new();
return;
}
}
And the errors I'm getting from the compiler
In Init.jack (line 10): In subroutine init: MyClass is not defined as a field, parameter or local or static variable
In Init.jack (line 10): In subroutine init: Expected [ or =
Is essentially because I can't access MyStatic if it's a member of MyClass....
At the end of the day my implementation works, but is not elegant. I can't get your implementation to work and I think this is due to me either
1) not understanding something about Jack ?
2) not understanding something about the implementation of your code ?
3) or maybe that code is wrong ? I.e. the idea you can initialise a static using a non member function as part of a constructor ? Maybe ?
The reason for this is
a) I'm trying to understand jack.
b) Trying to understand what you're trying to get across .
I'm sure there's lots I'm missing, and would like to know (if you have time) what you were trying to implement.
Kind regards, Lozminda
ps
I mean why have that function init ? (I know you say to initialise the static, but then just have let MyStatic = 1000) . When you declare var int Init, Init is zero, quoting:
7.2.6 VM Programming Examples (iii) the local variables that it is supposed to use are initialized to 0 and located in the local segment.
When I printInt(Init) it is indeed 0 and therefore
if (initFlag = 4242)
{
return;
}
is completely redundant no ? Surely I'm wrong ?
|
Administrator
|
Lozminda wrote
Hi
Apologies for the delay, paid work got in the way ! I've managed too code a slightly less ugly (but still ugly) implementation of the static behaviour i'm trying 'to get'
constructor MyClass new(String inputString, bool IsThisTheFirst_TimeAnObect_of_thisClassHasBeenConstructed )
{
if (IsThisTheFirst_TimeAnObect_of_thisClassHasBeenConstructed ) let MyStatic = StartingValue;
...
}
And in the main function
{
var MyClass a, b, c;
let a = MyClass.new("SomeInput", true); // It works, but not ideal
let b = MyClass.new("SomeInput", false);
let c = MyClass.new("SomeInput", false);
}
If you want the value of MyStatic associated with instances a to not be the same value that is associated with b and for those to not be the same value associated with c, then you cannot use a static variable because a static variable means that you have ONE variable that is accessed by all three. ANY of the three can change it, but that change is see by ALL of the other instances.
Let's say that you want to assign street addresses automatically when you create new houses with the first one starting at 12100 and each on after than going up by 50. This is easy to do. You have ONE static variable that you call an initialization function to set it to 12100 and then the constructor copies the current value into a field variable and increments the static value.
class House
{
static int nextAddress;
field int address;
function void init()
{
let nextAddress = 12100;
}
constructor void new()
{
let address = nextAddress;
let nextAddress = nextAddress + 50;
}
}
Now you just call House.init() once sometime before you create your first house.
nextAddress is a static variable. It is visible to all functions and methods in the House class.
address is a field variable. There is a separate variable for EACH instance of the object and ONLY methods can access the one associated with the instance it is called from.
NONE of these are automatically initialized to zero. That ONLY happens for local variables.
|
|
Awesome, thank you very much.
The block in my understanding was for some reason I thought one couldn't have methods and functions in the same class, hence me having an Init class in my example and not understanding how in your example a function init could be called in MyClass.
And to be fair to the course, in Fraction (an example supplied in the Project 9 folder), the class fraction has both methods and functions in that class. So my bad as they say.
Ah ha, it's all so clear now !
Thanks again for your perseverance.
Lozminda
|
|
This thread is a bit old, but I needed some random variables for a jack program and I remembered this quote from a previous thread
Since the initial values of static variables is unknown (IIRC), you are relying on it being very unlikely that the initFlag variable will just happen to be equal to 4242 by random change during the power on process (you have about a 1 in 65,000 chance of being unlucky enough for this assumption to be wrong).However it looks like that this is wrong. All variables are set to zero. If you look at the VMEmulator, they're all set to zero and when I've tried access an undefined static, that is zero too.
This here https://gist.github.com/ybakos/7ca67fcfd07477a9550b
which is a random number generator, which on the surface looks like it's using an undefined static to give a random number, but it isn't. It would be handy. I'm sure in other languages this is the case, but in the case of the VMEmulator running jack, it isn't
The initial values of static variables is zero, in case anyone needs to know or so it certainly seems. If I'm wrong happy to know how, but all the tests so far would indicate otherwise...
All the best, Lozminda
|
|
Ps
class NumBer
{
static int cheese;
function void init()
{
do Output.printString("cheese=");
do Output.printInt(cheese);
do Output.println();
return;
}
}
Outputs "cheese=0". Repeatedly. Surely it could be anything between -32762 & 32762 if I was wrong ?
|
Administrator
|
This post was updated on .
Lozminda wrote
This thread is a bit old, but I needed some random variables for a jack program and I remembered this quote from a previous thread
Since the initial values of static variables is unknown (IIRC), you are relying on it being very unlikely that the initFlag variable will just happen to be equal to 4242 by random change during the power on process (you have about a 1 in 65,000 chance of being unlucky enough for this assumption to be wrong).
However it looks like that this is wrong. All variables are set to zero.
There is NOTHING in the Jack Language specification that requires that uninitialized static variables be set to zero, so you can't write a program that relies on this being the case (unless you can otherwise ensure that this will always be true).
If you look at the VMEmulator, they're all set to zero and when I've tried access an undefined static, that is zero too.
That's an artifact of this particular implementation of this particular VMEmulator -- it is NOT a requirement of either the Jack language (or of the VM language, for that matter). Relying on this behavior, in any fashion, is a recipe for disaster. Remember, in a real system at the end of the day your code will be loaded into a ROM and then executed on a hardware platform using real RAM that, in almost all cases, will power up with effectively random contents and that will retain whatever values were previously stored in those memory locations due to prior code execution. So if your code relies on static variables being initialized to zero by the VMEmulator, then you are setting yourself up for a painful experience when it misbehaves while running it on the hardware (or possibly on the CPU Emulator or even the next version of the VMEmulator).
The same is true, by the way, with writing code that assumes that the static variables are not initialized to zero. If your code relies on there being a 1 in 65,000 chance of it being a certain value and will misbehave if it is, in fact, always initialized to zero, then you are writing bad code.
The use of an uninitialized static variable invokes undefined behavior. ANY behavior is consistent with the language specification, including throwing an error, producing a random result, starting a global thermonuclear war, or doing exactly what you would like it to do. In many ways, that last one is actually the worst case possible because it misleads you into believing that you actually understand the behavior and can rely on it when, in fact, you have no basis for that belief and can (and sooner or later almost certainly will) get bitten by it down the road.
This here https://gist.github.com/ybakos/7ca67fcfd07477a9550b
which is a random number generator, which on the surface looks like it's using an undefined static to give a random number, but it isn't. It would be handy. I'm sure in other languages this is the case, but in the case of the VMEmulator running jack, it isn't
That's actually an exceptionally poor random number generator (and Mark would have been the first to agree -- it's good enough to create an illusion of randomness suitable for what it was intended for, but it is actually a very thin illusion). It is fundamentally no better than a random number generator that always returns a value 7 greater than the last time it was called.
It doesn't rely on any assumption regarding undefined static variables, although there may be one that is implied. In most languages the PRNG (pseudorandom number generator) is expected to always produce the same sequence of random numbers when given the same seed. Some also specify that they should always start with the same seed if the user doesn't explicitly seed the generator. So Mark MAY have been relying on the seen being initialized to the same value by the system, but since he doesn't specify that this is expected behavior, it really doesn't matter because whether it does or not, no one can claim that it is doing anything other than what it was specified to do.
|
|
That's an artifact of this particular implementation of this particular VMEmulator -- it is NOT a requirement of either the Jack language (or of the VM language, for that matter). Relying on this behavior, in any fashion, is a recipe for disaster. Remember, in a real system at the end of the day your code will be loaded into a ROM and then executed on a hardware platform using real RAM that, in almost all cases, will power up with effectively random contents and that will retain whatever values were previously stored in those memory locations due to prior code execution. So if your code relies on static variables being initialized to zero by the VMEmulator, then you are setting yourself up for a painful experience when it misbehaves while running it on the hardware (or possibly on the CPU Emulator or even the next version of the VMEmulator). <quote author="WBahn">
I didn't realise the Jack programming language was used anywhere outside the Nand2Tetris course and on anything other than the VMEmulator etc..
I'd be real interested to hear where else it was being used ?
The use of an uninitialized static variable invokes undefined behavior. ANY behavior is consistent with the language specification, including throwing an error, producing a random result, starting a global thermonuclear war, or doing exactly what you would like it to do. In many ways, that last one is actually the worst case possible because it misleads you into believing that you actually understand the behavior and can rely on it when, in fact, you have no basis for that belief and can (and sooner or later almost certainly will) get bitten by it down the road.
I agree, but this doesn't seem to be the case using Jack in the VMEmulator. I'd be surprised if I managed to start a nuclear war using jack and the VMEmulator ! What amazing PR fo the N2T course..
Lozminda
|
|
Lozminda wrote
I didn't realise the Jack programming language was used anywhere outside the Nand2Tetris course and on anything other than the VMEmulator etc..
I'd be real interested to hear where else it was being used ?
Jack is not a particularly good language for development. And it doesn't have to be. It's raison d'ĂȘtre is to be familiar and easy to implement. I doubt if it's used anywhere outside the N2T course.
But that's not the point. The point is, that there are many implementations of Jack (after all, each student is supposed to write one). And even if they conform to the specification, they may (and will) differ in things that are left unspecified. The same is true for the VM, which is again implemented by students, so there are many VM to Hack translators, which will differ in various ways.
More over, this course as a whole and Jack in specific, is designed for learning. This include things to do and things to avoid doing. You should be able to take these lessons and apply them in the "real world". There, many languages have various implementations and they can run on various machines. Using unspecified behavior, which happens to work on your specific combination of these will lead to hard to find bugs.
|
Administrator
|
This post was updated on .
Lozminda wrote
I didn't realise the Jack programming language was used anywhere outside the Nand2Tetris course and on anything other than the VMEmulator etc..
I'd be real interested to hear where else it was being used ?
I know that several people have taken the Hack into working hardware implementations. Whether they use Jack as the high-level language for those or not, I have no idea. There would be pros and cons of doing so.
But that's neither here nor there -- drawing the conclusion that ALL implementations of anything MUST behave exactly as the ONE implementation you made some experiments on is a common mistake that people make and it leads them into all kinds of problems (sometimes with very real disastrous results in the real world). We've pretty much all been guilty of it at many times in our past -- I know that I drew many unjustified conclusions about how the C language worked based on my experience with the first compiler I used and for largely the exact same reason that you did here -- I assumed that every C compiler did exactly the same thing because, after all, they were C compilers. The thought that two different compilers could do very different things with the same program and still be "correct" simply wasn't conceivable to me -- until someone pointed it out and explained the concept of a language specification and the notions of defined, unspecified, and undefined behaviors. It was an epiphany that forever changed the way I look at programming.
Two examples of where I had done this (and which MANY people have done) is to assume that an int variable is a certain size (in my case 16 bits) because that's the size that it was on that first compiler. So of course and int has to always be 16 bits. So I wrote code that exploited the overflow behavior of 16-bit integers in order to write some pretty optimized code and that code completely broke a couple years later when we switched to a compiler that used 32 bits for an int. In a related fashion, I had code that unwittingly relied on the maximum value of a random integer being 32767 and that code all broke the first time it was run on a compiler that could produce higher values.
Finally, you want to avoid the easy-to-fall-into trap of taking that attitude, "Yeah, I know I shouldn't do this or that, but it works in THIS case and so there's no reason I shouldn't do it HERE." That kind of thinking leads to sloppy discipline that WILL carry over and lead you into doing things that you KNOW you shouldn't do in situations in which doing them causes problems.
|
|
What you're saying, in my opinion, is that MyStatic will grow by 1000 each time an object of MyClass uses SomeOtherMethod. Yes, I do want that.
I was able to get something to work, but it is horribly ugly and I don't think what I programmed is the right answer . It uses a getter, a function Object() { [native code] } with a variable sent to it, and three additional variables (ugg). If you'd like, I can post the code:
|
12
|