6.16 Free and Local Variables¶
When you want to refer to a variable that is not local to your function, use a free declaration. free Variables declared to be free free variable are assumed to be defined globally variable:free in the variable:global workspace. global variable
This is a global workspace variable.
counter := 0
0 |
Type: NonNegativeInteger
This function refers to the global counter.
f() ==
free counter
counter := counter + 1
Type: Void
The global counter is incremented by 1.
f()
Compiling function f with type () -> NonNegativeInteger
+++ |*0;f;1;G82322| redefined
1 |
Type: PositiveInteger
counter
1 |
Type: NonNegativeInteger
Usually FriCAS can tell that you mean to refer to a global variable and so free isn’t always necessary. However, for clarity and the sake of self-documentation, we encourage you to use it.
Declare a variable to be local when you do not want to refer to variable:local a global variable by the same name. local variable
This function uses counter as a local variable.
g() ==
local counter
counter := 7
Type: Void
Apply the function.
g()
7 |
Type: PositiveInteger
Check that the global value of counter is unchanged.
counter
1 |
Type: NonNegativeInteger
Parameters to a function are local variables in the function. Even if you issue a free declaration for a parameter, it is still local.
What happens if you do not declare that a variable x in the body of your function is local or free? Well, FriCAS decides on this basis:
- FriCAS scans your function line-by-line, from top-to-bottom. The right-hand side of an assignment is looked at before the left-hand side.
- If x is referenced before it is assigned a value, it is a free (global) variable.
- If x is assigned a value before it is referenced, it is a local variable.
Set two global variables to 1.
a := b := 1
1 |
Type: PositiveInteger
Refer to a before it is assigned a value, but assign a value to b before it is referenced.
h() ==
b := a + 1
a := b + a
Type: Void
Can you predict this result?
h()
Compiling function h with type () -> PositiveInteger
+++ |*0;h;1;G82322| redefined
3 |
Type: PositiveInteger
How about this one?
[a, b]
[3,1] |
Type: List PositiveInteger
What happened? In the first line of the function body for h, a is referenced on the right-hand side of the assignment. Thus a is a free variable. The variable b is not referenced in that line, but it is assigned a value. Thus b is a local variable and is given the value a+1=2. In the second line, the free variable a is assigned the value b+a which equals 2+1=3. This is the value returned by the function. Since a was free in h, the global variable a has value 3. Since b was local in h, the global variable b is unchanged—it still has the value 1.
It is good programming practice always to declare global variables. However, by far the most common situation is to have local variables in your functions. No declaration is needed for this situation, but be sure to initialize their values.
Be careful if you use free variables and you cache the value of your function (see ugUserCache ). Caching only checks if the values of the function arguments are the same as in a function call previously seen. It does not check if any of the free variables on which the function depends have changed between function calls.
Turn on caching for p.
)set fun cache all p
function p will cache all values.
Define p to depend on the free variable N.
p(i,x) == ( free N; reduce( + , [ (x-i)^n for n in 1..N ] ) )
Type: Void
Set the value of N.
N := 1
1 |
Type: PositiveInteger
Evaluate p the first time.
p(0, x)
x |
Type: Polynomial Integer
Change the value of N.
N := 2
2 |
Type: PositiveInteger
Evaluate p the second time.
p(0, x)
x |
Type: Polynomial Integer
If caching had been turned off, the second evaluation would have reflected the changed value of N.
Turn off caching for p.
)set fun cache 0 p
Caching for function p is turned off
FriCAS does not allow fluid variables, that is, variables variable:fluid bound by a function f that can be referenced by functions called by f. fluid variable
Values are passed to functions by reference: a pointer to the value is passed rather than a copy of the value or a pointer to a copy.
This is a global variable that is bound to a record object.
r : Record(i : Integer) := [1]
[i=1] |
Type: Record(i: Integer)
This function first modifies the one component of its record argument and then rebinds the parameter to another record.
resetRecord rr ==
rr.i := 2
rr := [10]
Type: Void
Pass r as an argument to resetRecord.
resetRecord r
[i=10] |
Type: Record(i: Integer)
The value of r was changed by the expression rr.i:=2 but not by rr:=[10].
r
[i=2] |
Type: Record(i: Integer)
To conclude this section, we give an iterative definition of Fibonacci numbers a function that computes Fibonacci numbers. This definition approximates the definition into which FriCAS transforms the recurrence relation definition of fib in ugUserRecur .
Global variables past and present are used to hold the last computed Fibonacci numbers.
past := present := 1
1 |
Type: PositiveInteger
Global variable index gives the current index of present.
index := 2
2 |
Type: PositiveInteger
Here is a recurrence relation defined in terms of these three global variables.
fib(n) ==
free past, present, index
n < 3 => 1
n = index - 1 => past
if n < index-1 then
(past,present) := (1,1)
index := 2
while (index < n) repeat
(past,present) := (present, past+present)
index := index + 1
present
Type: Void
Compute the infinite stream of Fibonacci numbers.
fibs := [fib(n) for n in 1..]
[1,1,2,3,5,8,13,21,34,55,…] |
Type: Stream PositiveInteger
What is the 1000th Fibonacci number?
fibs 1000
434665576869374564356885276750406258025646605173717804024_
8172908953655541794905189040387984007925516929592259308_
0322634775209689623239873322471161642996440906533187938_
298969649928516003704476137795166849228875
Type: PositiveInteger
As an exercise, we suggest you write a function in an iterative style that computes the value of the recurrence relation having the initial values . How would you write the function using an element OneDimensionalArray or Vector to hold the previously computed values?