GNU Prolog provides a simple and powerful way to assign and read global variables. A global variable is associated with each atom, its initial value is the integer 0. A global variable can store 3 kinds of objects:
The space necessary for copies and arrays is dynamically allocated and recovered as soon as possible. For instance, when an atom is associated with a global variable whose current value is an array, the space for this array is recovered (unless the assignment is to be undone when backtracking occurs).
When a link to a term is associated with a global variable, the reference to this term is stored and thus the original term is returned when the content of the variable is read.
Global variable naming convention: a global variable is referenced by an atom.
If the variable contains an array, an index (ranging from 0) can be provided using a compound term whose principal functor is the correponding atom and the argument is the index. In case of a multi-dimensional array, each index is given as the arguments of the compound term.
If the variable contains a term (link or copy), it is possible to only reference a sub-term by giving its argument number (also called argument selector). Such a sub-term is specified using a compound term whose principal functor is -/2 and whose first argument is a global variable name and the second argument is the argument number (from 1). This can be applied recursively to specify a sub-term of any depth. In case of a list, a argument number I represents the Ith element of the list. In the rest of this section we use the operator notation since - is a predefined infix operator (section 7.14.10).
In the following, GVarName represents a reference to a global variable and its syntax is as follows:
GVarName | ::= | atom | whole content of a variable |
atom(Integer,…,Integer) | element of an array | ||
GVarName-Integer | sub-term selection | ||
Integer | ::= | integer | immediate value |
GVarName | indirect value |
When a GVarName is used as an index or an argument number (i.e. indirection), the value of this variable must be an integer.
Here are some examples of the naming convention:
a | the content of variable associated with a (any kind) |
t(1) | the 2nd element of the array associated with t |
t(k) | if the value associated with k is I, the Ith element of the array associated with t |
a-1-2 | if the value associated with a is f(g(a,b,c),2), the sub-term b |
Here are the errors associated with global variable names and common to all predicates.
GVarName is a variable | instantiation_error | |
GVarName is neither a variable nor a callable term | type_error(callable, GVarName) | |
GVarName contains an invalid argument number (or GVarName is an array) | domain_error(g_argument_selector, GVarName) | |
GVarName contains an invalid index (or GVarName is not an array) | domain_error(g_array_index, GVarName) | |
GVarName is used as an indirect index or argument selector and is not an integer | type_error(integer, GVarName) | |
Arrays: the predicates g_assign/2, g_assignb/2 and g_link/2 (section 7.21.2) can be used to create an array. They recognize some terms as values. For instance, a compound term with principal functor g_array is used to define an array of fixed size. There are 3 forms for the term g_array:
An array can be extended explicitely using a compound term with principal functor g_array_extend which accept the same 3 forms detailed above. In that case, the existing elements of the array are not initialized. If g_array_extend is used with an object which is not an array it is similar to g_array.
Finally, an array can be automatically expanded when needed. The programmer does not need to explicitely control the expansion of an automatic array. An array is expanded as soon as an index is outside the current size of this array. Such an array is defined using a compound term with principal functor g_array_auto:
In any case, when an array is read, a term of the form g_array([Elem0,..., ElemSize-1]) is returned.
Some examples using global variables are presented later (section 7.21.7).
Templates
Description
g_assign(GVarName, Value) assigns a copy of the term Value to GVarName. This assignment is not undone when backtracking occurs.
g_assignb/2 is similar to g_assign/2 but the assignment is undone at backtracking.
g_link(GVarName, Value) makes a link between GVarName to the term Value. This allows the user to give a name to any Prolog term (in particular non-ground terms). Such an assignment is always undone when backtracking occurs (since the term may no longer exist). If Value is an atom or an integer, g_link/2 and g_assignb/2 have the same behavior. Since g_link/2 only handles links to existing terms it does not require extra memory space and is not expensive in terms of execution time.
NB: argument selectors can only be used with g_assign/2 (i.e. when using an argument selector inside an assignment, this one must not be backtrackable).
Errors
See common errors detailed in the introduction (section 7.21.1)
GVarName contains an argument selector and the assignment is backtrackable | domain_error(g_argument_selector, GVarName) | |
Portability
GNU Prolog predicates.
Templates
Description
g_read(GVarName, Value) unifies Value with the term assigned to GVarName.
Errors
See common errors detailed in the introduction (section 7.21.1)
Portability
GNU Prolog predicate.
Templates
Description
g_array_size(GVarName, Value) unifies Size with the dimension (an integer > 0) of the array assigned to GVarName. Fails if GVarName is not an array.
Errors
See common errors detailed in the introduction (section 7.21.1)
Size is neither a variable nor an integer | type_error(integer, Size) | |
Portability
GNU Prolog predicate.
Templates
Description
g_inc(GVarName, Old, New) unifies Old with the integer assigned to GVarName, increments GVarName and then unifies New with the incremented value.
g_inc(GVarName, New) is equivalent to g_inc(GVarName, _, New).
g_inco(GVarName, Old) is equivalent to g_inc(GVarName, Old, _).
g_inc(GVarName) is equivalent to g_inc(GVarName, _, _).
Predicates g_dec are similar but decrement the content of GVarName instead.
Errors
See common errors detailed in the introduction (section 7.21.1)
Old is neither a variable nor an integer | type_error(integer, Old) | |
New is neither a variable nor an integer | type_error(integer, New) | |
GVarName stores an array | type_error(integer, g_array) | |
GVarName stores a term T which is not an integer | type_error(integer, T) | |
Portability
GNU Prolog predicates.
Templates
Description
g_set_bit(GVarName, Bit) sets to 1 the bit number specified by Bit of the integer assigned to GVarName to 1. Bit numbers range from 0 to the maximum number allowed for integers (this is architecture dependent). If Bit is greater than this limit, the modulo with this limit is taken.
g_reset_bit(GVarName, Bit) is similar to g_set_bit/2 but sets the specified bit to 0.
g_test_set_bit/2 succeeds if the specified bit is set to 1.
g_test_reset_bit/2 succeeds if the specified bit is set to 0.
Errors
See common errors detailed in the introduction (section 7.21.1)
Bit is a variable | instantiation_error | |
Bit is neither a variable nor an integer | type_error(integer, Bit) | |
Bit is an integer < 0 | domain_error(not_less_than_zero, Bit) | |
GVarName stores an array | type_error(integer, g_array) | |
GVarName stores a term T which is not an integer | type_error(integer, T) | |
Portability
GNU Prolog predicates.
Simulating g_inc/3: this predicate behaves like: global variable:
my_g_inc(Var, Old, New) :- g_read(Var, Old), N is Value + 1, g_assign(Var, X), New = N.
The query: my_g_inc(c, X, _) will succeed unifying X with 0, another call to my_g_inc(a, Y, _) will then unify Y with 1, and so on.
Difference between g_assign/2 and g_assignb/2: g_assign/2 does not undo its assignment when backtracking occurs whereas g_assignb/2 undoes it.
test(Old) :- | testb(Old) :- | |
g_assign(x,1), | g_assign(x,1), | |
( g_read(x, Old), | ( g_read(x, Old), | |
g_assign(x, 2) | g_assignb(x, 2) | |
; g_read(x, Old), | ; g_read(x, Old), | |
g_assign(x, 3) | g_assign(x, 3) | |
). | ). |
The query test(Old) will succeed unifying Old with 1 and on backtracking with 2 (i.e. the assignment of the value 2 has not been undone). The query testb(Old) will succeed unifying Old with 1 and on backtracking with 1 (i.e. the assignment of the value 2 has been undone).
Difference between g_assign/2 and g_link/2: g_assign/2 (and g_assignb/2) creates a copy of the term whereas g_link/2 does not. g_link/2 can be used to avoid passing big data structures (e.g. dictionaries,…) as arguments to predicates.
test(B) :- | test(B) :- | |
g_assign(b, f(X)), | g_link(b, f(X)), | |
X = 12, | X = 12, | |
g_read(b, B). | g_read(b, B). |
The query test(B) will succeed unifying B with f(_) (g_assign/2 assigns a copy of the value). The query testl(B) will succeed unifying B with f(12) (g_link/2 assigns a pointer to the term).
Simple array definition: here are some queries to show how arrays can be handled:
| ?- g_assign(w, g_array(3)), g_read(w, X). X = g_array([0,0,0]) | ?- g_assign(w(0), 16), g_assign(w(1), 32), g_assign(w(2), 64), g_read(w, X). X = g_array([16,32,64])
this is equivalent to:
| ?- g_assign(k, g_array([16,32,64])), g_read(k, X). X = g_array([16,32,64]) | ?- g_assign(k, g_array(3,null)), g_read(k, X), g_array_size(k, S). S = 3 X = g_array([null,null,null])
2-D array definition:
| ?- g_assign(w, g_array(2, g_array(3))), g_read(w, X). X = g_array([g_array([0,0,0]),g_array([0,0,0])]) | ?- ( for(I,0,1), for(J,0,2), K is I*3+J, g_assign(w(I,J), K), fail ; g_read(w, X) ). X = g_array([g_array([0,1,2]),g_array([3,4,5])]) | ?- g_read(w(1),X). X = g_array([3,4,5])
Hybrid array:
| ?- g_assign(w,g_array([1,2,g_array([a,b,c]), g_array(2,z),5])), g_read(w, X). X = g_array([1,2,g_array([a,b,c]), g_array([z,z]),5]) | ?- g_read(w(1), X), g_read(w(2,1), Y), g_read(w(3,1), Z). X = 2 Y = b Z = z | ?- g_read(w(1,2),X). uncaught exception: error(domain_error(g_array_index,w(1,2)),g_read/2)
Array extension:
| ?- g_assign(a, g_array([10,20,30])), g_read(a, X). X = g_array([10,20,30]) | ?- g_assign(a, g_array_extend(5,null)), g_read(a, X). X = g_array([10,20,30,null,null]) | ?- g_assign(a, g_array([10,20,30])), g_read(a, X). X = g_array([10,20,30]) | ?- g_assign(a, g_array_extend([1,2,3,4,5,6])), g_read(a, X). X = g_array([10,20,30,4,5,6])
Automatic array:
| ?- g_assign(t, g_array_auto(3)), g_assign(t(1), foo), g_read(t,X). X = g_array([0,foo,0]) | ?- g_assign(t(5), bar), g_read(t,X). X = g_array([0,foo,0,0,0,bar,0,0]) | ?- g_assign(t, g_array_auto(2, g_array(2))), g_assign(t(1,1), foo), g_read(t,X). X = g_array([g_array([0,0]),g_array([0,foo])]) | ?- g_assign(t(3,0), bar), g_read(t,X). X = g_array([g_array([0,0]),g_array([0,foo]),g_array([0,0]),g_array([bar,0])]) | ?- g_assign(t(3,4), bar), g_read(t,X). uncaught exception: error(domain_error(g_array_index,t(3,4)),g_assign/2) | ?- g_assign(t, g_array_auto(2, g_array_auto(2))), g_assign(t(1,1), foo), g_read(t,X). X = g_array([g_array([0,0]),g_array([0,foo])]) | ?- g_assign(t(3,3), bar), g_read(t,X). X = g_array([g_array([0,0]),g_array([0,foo]),g_array([0,0]), g_array([0,0,0,bar])]) | ?- g_assign(t, g_array_auto(2, g_array_auto(2, null))), g_read(t(2,3), U), g_read(t, X). U = null X = g_array([g_array([null,null]),g_array([null,null]), g_array([null,null,null,null]),g_array([null,null])])