Advanced topics

Including files

The function Include[file->TEXT] is used to insert the contents of the file given into the input stream, as if they had been typed directly by the user. This can be particularly useful in conjunction with user defined functions, in that a library of useful functions can be constructed and included into the program asily.

Include may only appear at the ``top level'' of the program. That is to say, it cannot be used inside a loop, function declaration, or expression. However, files may be nested arbitrarily deep using Include, so you can include a file which in turn includes other files.

When a file is included, the GCL looks for a file with that name in the following locations, in order.

  1. The current directory;

  2. The directory specified by the \verb+HOME+ environment variable, if any;

  3. The directory specified by the \verb+GCLLIB+ environment variable, if any;

  4. The directory where the executable is located.

This search order applies also to the gclini.gcl file that is loaded whenever the GCL is started. Hence, you can keep a standard version of a file that is frequently included in the same directory as the GCL executable, and then modify it for use on a particular project by keeping a modified copy of it in the directory associated with that project.

It is good practice to have included files identify themselves when they are included. This can be done by using the GetPath function, which returns the full pathname of the file from which the command is executed. For example, if the following line is placed as the first line in an included file (say with filename path/myfile.gcl)

StdOut << "Include[\""&GetPath[]&"\"]\n"
(Note that the standard escape sequences \" and \n are used within a text string to represent a quotation mark and a carriage return, respectively.) Then when the file is included, it will identify itself by writing the following message to the standard output stream:
Include["path/myfile.gcl"]

Flow control structures

The GCL contains three functions which allow flow control within a program. These functions are ``special'' in that their parameters are evaluated in a special way (since their parameters are expressions and sequences of statements); they also have no ``formal'' names for their parameters.

Conditional execution with If

The function If allows execution of a sequence of statements only under certain conditions. The syntax of the function is

If[boolean-expression, statement-list {, statement-list}]
The function is interpreted as follows: If the boolean-expression evaluates to True, then the first list of statements is executed. If it evaluates to False and the second (optional) list of statements is present, that list is executed; if it is not present, the If expression evaluates to False. For example, the statement
If[i = 2, j := 1, j := 2]
sets \verb+j+ to \verb+1+ if the value of \verb+i+ is \verb+2+, and sets \verb+j+ to \verb+2+ if the value of \verb+i+ is not \verb+2+. It would also be perfectly legitimate to write
If[i = 2, j := 1]
in which case \verb+j+ would be set to \verb+1+ if \verb+i+ is equal to \verb+2+, but if \verb+i+ were not \verb+2+, the expression evaluates to \verb+False+.

This last example brings up an important note about conditional execution. Expressions which appear in the branch of an If expression which is not taken are treated as if they did not exist. So, were this the first mention of \verb+j+ in this scope, the last example would leave \verb+j+ defined only if \verb+i+ was equal to \verb+2+. However, \verb+j+ would not be defined otherwise. It is therefore necessary to be careful in constructing If expressions which result in the definition of new variables.

Repetitive execution with While

Often in writing programs it is necessary to execute a block of statements repeatedly, usually with different values for variables. To this end the GCL provides a special function While as a generalized looping construct. The syntax of the function is

While[boolean-expression, statement-list]
The function is interpreted as follows: While boolean-expression evalutes to True, execute the statements in statement-list. Note that the evaluation of the boolean takes place at the beginning of the execution of a block, so it is only important whether the value is True or False at the beginning of the block, and not at some point in the middle.

This simple loop creates a list of the first ten perfect squares:

i := 1;
list := { };
While[i <= 10, list := list & { i^2 }; i := i + 1;]

As with If, be wary of declaring a variable implicitly inside a loop. A variable is declared only when the corresponding statement is executed; so, if the boolean-expression is False the first time it is evaluated, the body of the loop never executes, and no variables which appear in the loop are considered to be defined.

Indexed looping with For

The function For is a specialized looping construct, useful mostly in cases where some index variable is used to iterate a list or some sequence. The general syntax for For is

For[statement-list, boolean-expression, statement-list, statement-list]
The call is interpreted as follows:

  1. Execute the statements in the first statement-list (initialization);

  2. Evaluate the boolean-expression (or guard);

  3. If the boolean-expression is False, terminate the loop and continue with the first statement after the loop. If it is True, execute the third statement-list, which is the body of the loop;

  4. Execute the second statement-list, which is usually used to increment a counter, and return to the evaluation of the guard in step 2.

Returning to the example in the While section, here is another way of writing the loop to create a list of the first ten squares:

For[i:=1; list:={}, i<=10, i:=i+1, list:=list&{i^2}]
The two methods are completely equivalent, but it is often more convenient to use the structure afforded by the For function.

Input and output

Data can be read from an input stream by the Read function. So if \verb+in+ is an input stream (i. e., of type INPUT) then a successful call of \verb+Read[in,x]+ will read the exposed data (see function reference for definition of ``exposed data'') from the input stream, \verb+in+, assign \verb+x+ to have that type and value, and position the file pointer at the end of the exposed data, to be ready for the next call of Read+. \verb+Read[in,x]+ has the short form \verb+in >> x+. Since the return value of \verb+Read[in,x]+ is \verb+in+, these commands can be chained. In other words,

in >> x >> y
is equivalent to
Read[in,x]
Read[in,y]

In the statement \verb+Read[in,x]+, if \verb+x+ is undefined, then its data type is determined from the exposed data in the input stream. On the other hand if \verb+x+ is previously defined, then the Read function will expect to find the corresponding data type in the input stream, and a file read error will be generated if the exposed data is of the wrong data type. If \verb+x+ is previously defined to be a \verb+LIST(T)+, then \verb+Read[in,x]+ will successively read elements into each element of the list. If the wrong data type is found for any element of the list, a file read error will be returned. Thus, if the file file.dat contains the following data

25 1/3 "This is a text string!" False 3.14159 
{{1, 0},{0, 1}}
1 2 3 4 5
then the following GCL code
in:=Input["file.dat"]
x:=List[0,5]
in >> i >> r >> t >> b >> f >> l >> x
opens an input stream, consisting of the contents of file.dat, and then reads data from the input stream into the corresponding variables. After the last statement, \verb+i+ is a NUMBER with value \verb+25+, \verb+r+ is a NUMBER with value \verb+1/3+, \verb+t+ is a TEXT with value "This is a text string!", \verb+b+ is a BOOLEAN with value False, \verb+f+ is a NUMBER with value \verb+3.14159+, \verb+l+ is a LIST(LIST(NUMBER)) with value \verb+{{1,0},{0,1}}+, and \verb+x+ is a LIST(NUMBER) with value \verb+{1,2,3,4,5}+.

Data can be written to an output stream by the use of the Write function. Thus, the following commands

out:=Output["file.out"]
x:=0/1;y:={0.0,0.0}
Write[out,x]
Write[out,y]
creates an output stream, \verb+out+, and then writes out a NUMBER, followed by a list of two NUMBERs. \verb+Write[out,x]+ has the short form \verb+out << x+. Since the return value of Write is \verb+out+, these commands can be chained. So the two lines writing out \verb+x+ and \verb+y+ in the above example could be written instead
out << x << y

The Format and ListFormat functions can be used to control the formatting of data written to an output stream. Note that the Read and Write functions are not listable.

The Read and Write functions can only be used with certain data types (see function documentation). Reading and writing of EFG and NFG from external files can be done with the LoadEfg, SaveEfg, LoadNfg and SaveNfg functions.

Null values

Certain function calls in the GCL result in either invalid or undetermined values for the required data type. Examples would be asking for the first child node of a terminal node, or the parent of the root node, or an outcome attached to a node with no outcome. Similar problems arise in the algorithmic and computational parts of the code. Here, for example, algorithms that do not compute sequential equilibria do not return belief probabilities for unreached information sets. Other algorithms may action probabilities for off the equilibrium path information sets undetermined.

Under certain situations such as the above, when no valid value is available for return, instead of returning an error, the GCL will a Null value for the given type. Returning a Null value instead of terminating execuation with an error message is frequently desirable in a setting where functions are being called listably, as it allows for computation of elements of the list that have valid entries without aborting the GCL because some elements have invalid entries.

The situations in which Null values are returned are described in the function reference section of the manual. Most functions in the GCL, when encountering a Null value as a parameter, will abort with an error message. Whether a function will accept Null values as valid values for an argument is indicated in the function prototype with an asterisk (*) after the relevant parameter. For example, the function prototype for IsNull is

IsNull[x->T*]=:BOOLEAN
  for any data type, T
Here, the * indicates that this function accepts Null values for its argument. If a built-in function accepts Null values for an argument, it is documented in the function reference section what the behavior of the function is when encountering a Null value.

Note that user defined functions will accept Null values for arguments also, if the relevant parameter is designated with a \verb+*+, as above.

System information

There are several commands in the GCL that allow you to get information from or run processes on the host system.

A series of functions, StartWatch, StopWatch, and ElapsedTime, provide information on the amount of cpu time used by the GCL, and can be used to time computation. Another series of functions, GetEnv, SetEnv, UnSetEnv, Platform, allow you to set and check environment variables, or check the operating system that the GCL is running on. A third series of functions, ExePath, GetPath, and GambitExe (included in stdudfs.gcl), provide path and filenames to files being used by or related to the GCL. Finally, the Shell command allows you to run a child process from the GCL.

The above system functions are also used in the user defined function Display, which allows you to start a child process that launches up the Gambit graphical interface and loads an extensive or normal form game. If \verb+game+ is a variable of type NFG or EFG, then the call Display[game] will load the \verb+game+ in the graphical interface.\footnote{The user defined functions GambitExe and Display assume that you used the default directories and filenames when you installed Gambit. If you deviated from the standard installation, you may have to edit these functions before they work correctly.}

Variables pointing to deleted objects

In the GCL, you can create variables to represent objects which may be deleted or invalidated subsequently as side effects of some functions. For example, suppose you define a variable to represent an element (node, information set, outcome, etc.) of an extensive form game. You may subsequently delete the part of the tree that contains that element. The object that the variable originally referred to is no longer valid. In this case, the variable is automatically unassigned.

In the following example, a game is loaded, and the variable \verb+n+ is defined to be the first child of the first child of the root node. Now the part of the tree after the first branch is deleted, the variable \verb+n+ is now undefined:

GCL1:= e:=LoadEfg["poker.efg"]
GCL2:= r:=RootNode[e]
GCL3:= n:=r#1#1
GCL4:= DeleteTree[r#1]
GCL5:= << n
ERROR: Print[]: Undefined reference "n" passed for parameter #1
Since the node is no longer present in the tree, the variable \verb+n+ which refers to it has been unassigned. A similar situation occurs here, where elements of a list are deleted via DeleteTree:
GCL1:= e:=LoadEfg["poker.efg"]
GCL2:= t:=TerminalNodes[e]
GCL3:= DeleteTree[RootNode[e]#1]
GCL4:= << t
ERROR: Print[]: Undefined reference "t" passed for parameter #1
In this case, a variable \verb+t+ is defined to refer to the list of terminal nodes. The function DeleteTree deletes some of the terminal nodes. Even though some of the nodes in the list \verb+t+ are still valid, if any element of a list is deleted, the entire list is unassigned.

Similar situations arise with mixed and behavior strategies if the underlying game to which they refer change in a way to invalidate the solutions. In the following example, a solution to the game \verb+e+ is saved as a variable \verb+b+ of type BEHAV:

GCL1:= e:=LoadEfg["poker.efg"]
GCL2:= << b:=EnumMixedSolve[e,asNfg->True]_1
(Behav) { { 1.000 0.000 }{ 0.333 0.667 } }{ { 0.667 0.333 } }
GCL3:= DeleteTree[RootNode[e]#1]
GCL4:= << b
ERROR: Print[]: Undefined reference "b" passed for parameter #1
The game is subsequently edited, making the profile no longer valid for the changed game; therefore, the variable containing it is automatically unassigned.

Errors

Sometimes, programs contain errors. The interpreter is able to detect errors which violate rules of the language, and reports information about the error so the user can correct it. There are two main classes of errors which may arise in GCL programs: parse errors and run time errors.

A parse error occurs when the GCL interpreter cannot determine the meaning of a statement, because it does not conform to the grammatical rules of the language. This most frequently occurs when braces, brackets, or quotation marks fail to match up or when punctuation is missing. When it encounters a parse error, the GCL interpreter will display an error message giving the filename and line number of the error and the point at which it determined an error it occurred.

A run time error occurs when a statement which obeys the grammatical rules of the language violates another rule of the language during its execution. Examples of this type of error include attempting to use a variable name which is not defined, and using a value of one type where another type was expected. Also, builtin functions may generate this type of error on invalid inputs. A run time error will result in termination of the GCL at the point where the error occurs, with a message indicating the type of error encountered.