Variables

From FizzFuzz

Jump to: navigation, search

Variables in FizzFuzz are defined similarly to those in C, C++, Java, and others in the C family of programming languages. They require a declaration of the data type, a name, and possibly a value to assign to it. A difference from other languages in the C family is that all variables are instances of an object, of the class of that data type (a class that is, generally, non-modifiable). As a result, variables have associated functions which can directly modify the value of the variable. Variables can also be easily passed by reference, passed by value, and passed by address.

Contents

Defining A Variable

There are several basic types of variables, which can be declared a few different ways. The most common type of variable is simply a plain variable, which is not an instance and not an array. This basic type is called a data type instance, as it is an instance of a data type, as opposed to an instance of a class.

[access] [modifier] datatype identifier [= value];

Where:

  • access is the level of access to the variable. If this is not provided, then it defaults to public.
  • modifier is a modifier to the variable, if that type is valid for the data type. If nothing is provided, then this is set as nothing.
  • datatype is the data type of the variable. See the relevant article for a listing of default and available data types.
  • identifier is the identifier of the variable, also known as the variable name. Identifiers in FizzFuzz are case sensitive, and they can only contain A through Z, a through z, 0 through 9, and _ (underscores). Identifiers can not start with a number (0 through 9), but they can start with any other valid character.
  • value is the value to assign to the variable. This can be any value valid for the data type. If nothing is provided, then the value defaults to null.

Defining An Array

Full Article: Arrays

Arrays are a data structure that stores an arbitrary amount of values of the data type they are declared as, indexed by a number indicating their position in the array. Unlike C and C++, arrays in FizzFuzz begin indexing at one, meaning there is not a zeroth index on the array. A basic array definition is as follows:

[access] [modifier] datatype identifier[[size]] [= {[elem1, elem2, ...]}];

Where:

  • access, modifier, datatype, and identifier are as above.
  • [] indicates an array definition.
  • size is the size of the array. If not included, one of two things happen:
    1. If immediately assigned an array, then the program will continue as normal.
    2. If not assigned an array, an error will occur.
  • {elem1, elem2, ...} are the values of the array.

For additional information on initializing an array, see the article on arrays.

Defining A Class Instance

Full Article: Classes

Class instances are also defined as variables, with the data type being considered the class.

[access] class identifier[(arg1, arg2, ...)] [= {new[([arg1, arg2, ...])], value, null}]

Where:

  • access and identifier are the same as above.
  • class is the name of the class being indicated by the variable.
  • new is the new statement, which creates the class.
    • arg1, arg2, ... are the arguments for the function. These are not required if not required by the class. The case of being placed directly after the identifier is only if it is assigned a value. See below.
  • value is a value assigned to the class. This is only valid syntax if the class's operator=() method is defined.
  • null is the null value, an explicit indication of an assignment of null. This also happens if the instance is not initialized right away.

Modifiers

Modifiers are a value that alter how a data type works and stores its information. By default, only a few of these exist in the language, though modifications to the standard library can add more. The current ones are:

  • long — Increases the amount of space used to store an int and a float. Several can be used in succession to increase the space stored further.
  • short — Indicates a standard-length int or float.
  • unsigned — Causes int and float to no longer store information on the sign. This allows for an extra bit to be freed up to store more digits.
  • signed — Indicates that int and float store information about the sign of the number they're storing.
  • const — This means that, once the variable is defined, it can not be changed, at compile-time or runtime. Typically, constant variables are written in all caps.
  • quasi — Short for quasi-constant. Quasis are constant-like, in that they can not be changed at runtime. The difference, though, is that they can be redefined at compile time. This is useful for altering values of derived classes without having to worry about the value being changed at runtime.

Passing and Assignment

Pass-By-Value

Pass-by-value is the default method for non-class instance variables to be passed and assigned, which means that data types instances are duplicated and have this duplicate stored in a new variable. This is explicitly indicated via the $ prefix operator. For example, the code below will output "foo and bar are different".

int foo = 10; // Defines foo, which is equal to 10.
int bar = foo; // Sets foo equal to bar.
 
// Equivalently
int bar = $foo; // Explicitly forces foo to be passed-by-reference, instead of implicitly as is usual.
 
if(&foo == &bar)
    // If the address of foo and bar are the same, then they are the same data type instance.
    output << "foo and bar are the same.";
else
    // Otherwise, they're different.
    output << "foo and bar are different.";

The $ operator can also be used in the declaration of variables, forcing the value stored in it to be a duplicate of the data type instance being stored in it.

int foo = 10;
int $bar = 15; // bar forced any passing, non-literal value to be passed by value, as it is defined as $bar.
 
bar = foo; // This is equivalent to bar = $foo;, which is the implicit default.
bar = *foo; // This causes an error, as pass-by-reference is not valid for the definition of bar.

Class Instance Duplication

This can also be used to create a duplicate instance of a class. The default method for storing class instances is pass-by-reference, but it is possible to use the $ operator to override this.

class foo
{
    public int bar = null;
 
    void +foo(int b)
    {
        bar = b;
    }
}
 
foo alpha = new(10);
foo beta = new(11);
 
output << alpha.bar;            // Outputs "10".
output << beta.bar;             // Outputs "11".
 
beta = alpha;
 
output << alpha.bar;            // Outputs "10".
output << beta.bar;             // Outputs "10".
output << "[&alpha] :: [&beta]" // Outputs "0xFFA344 :: 0xFFA3FF".
 
beta = $alpha;
 
output << alpha.bar;            // Outputs "10".
output << beta.bar;             // Outputs "10".
output << "[&alpha] :: [&beta]" // Outputs "0xFFB433 :: 0xFFA3FF".

Pass-By-Reference

Pass-by-reference causes a reference to the data type instance to be stored in a new variable. This is the default method for class instances, but is not the default method for data type instances. What this means is that if we have two variables α and β, and α is stored in β, altering the value of β will alter the value of α. Pass-by-reference for a non-class instance is done using the * prefix operator.

int foo = 10;
int bar = *foo;
 
bar *= 25;
 
output << foo; // This will output "250".

As with the $ operator, the * operator can be used in a variable declaration to force a variable to only allow pass-by-reference. As an example, the following code will output "foo is the same as bar."

int foo = 10;
int *bar;
 
bar = foo; // Equivalent to bar = *foo;
 
if(&foo == &bar)
    output << "foo is the same as bar.";
else
    output << "foo is different from bar.";

As above, with pass-by-value, using the $ operator when a variable has been defined using * can lead to errors.

int foo = 5;
int *bar = 10; // Passing a literal always works, regardless of the variable pass-by setting.
 
bar = $foo; // This causes an error, as bar is defined as pass-by-reference, and must accept a reference.

Pass-By-Address

Values can also be passed by address. This does not serve as the default action for any type of assignment, and therefore must always be made explicit. The following is an example of this, which will output "foo is storing bar".

int foo = 25;
int bar = &foo;
 
if(bar == &foo)
    output << "foo is storing bar";
else
    output << "foo is not storing bar";

Unlike pass-by-reference and pass-by-value, declaring a variable with the & operator does allow values assigned using the * and $ operators.

int foo = 25;
int &bar = foo; // Defines bar as an address-only variable.
 
output << "[&foo] :: [bar]"; // Outputs "0xF3D8DB :: 0xF3D8DB".
 
bar = $foo; // This creates a duplicate of foo, then stores the address.
output << "[&foo] :: [bar]"; // Outputs "0xF3D8DB :: 0xC9D00A".

Address Resolution

The * operator, both in variable declaration and assignment, can be used to resolve an address, so that the related methods, variables, and etc. of that variable can be used.

class foo
{
    public int bar = null;
 
    void +foo(int b)
    {
        bar = b;
    }
}
 
foo foo = new(15);
foo bar = &foo; // This passes foo as an address to bar.
 
output << bar; // This outputs "0xF34AF3".
output << *bar.bar; // This outputs "15".
 
bar = *bar; // This resolves the address at &foo back into a reference.
 
output << bar.bar; // This outputs "15".

Memory Alteration

One significant use of the & operator is that it allows the data directly at the memory address of the variable to be altered. This can be tricky, as well as dangerous, but can be used to great effect when used carefully. The notation for this is as follows:

&identifier = value;

Where identifier is as above, and value is either a variable or a literal. This will alter the value stored at the address of identifier to that stored at value. At first, this seems to be effectively equivalent to...

identifier = value;

... but this is only true if the data types of identifier and value are the same. The default FizzFuzz method of variable assignment is, when a variable is assigned a given value, if the data type of that value is not the same as the variable, then the value is recast. If the memory-altering method is used, then this is bypassed, and the value stored directly at the variable, possibly changing the data type of the variable. This also allows a variable to recast itself, using the notation below:

datatype1 identifier = value;
&identifier = (datatype2)identifier;

Where datatype1 and datatype2 are two different data types, identifier is the same as above, and value is a non-null value, either a literal or another variable. An example of this in use can be seen below.

int foo = 97; // Creates an integer variable which stores 97.
 
output << "[&foo] -> [foo]"; // Outputs "0x3C045A -> 97".
 
&foo = (char)foo; // Sets the memory address of foo to the memory address of a recast foo, which changes foo from an integer to a character.
output << "[&foo] -> [foo]"; // Outputs "0x3C045A -> a". This is because 'a' character indicated by the ASCII value 97.
 
foo = 98; // As foo is now a char, this implicitly recast to a char, meaning 'b' is stored.
output << "[&foo] -> [foo]"; // Outputs "0x3C045A -> b"

Equivalently, the var.ReCast() function can be used to do this.

int foo = 97;
 
foo.ReCast(char); // Recasts foo to a char.
output << "[&foo] -> [foo]"; // Outputs "0x3C045A -> a".

Equivalent Operations For $, *, And &

Note the following, for variables α and β:

  • α = *&β is the same as α = *β.
  • α = &*β is the same as α = &β.
  • α = $&β is undefined.
  • α = $*&β is the same as α = $β.
  • α = &$β is not the same as α = &β.
  • α = *$β is the same as α = *β.
  • α = $*β is the same as α = $β
Personal tools