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:

  1. 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.
  2. If x is referenced before it is assigned a value, it is a free (global) variable.
  3. 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?