As an example of how to annotate an ECLiPSe program, consider the following classic cryptographic example, SEND+MORE=MONEY
sendmore(Digits) :-
Digits = [S,E,N,D,M,O,R,Y],
Digits :: [0..9],
Carries = [C1,C2,C3,C4],
Carries :: [0..1],
alldifferent(Digits),
S #\= 0,
M #\= 0,
C1 #= M,
C2 + S + M #= O + 10*C1,
C3 + E + O #= N + 10*C2,
C4 + N + R #= E + 10*C3,
D + E #= Y + 10*C4,
labeling(Carries),
labeling(Digits).
It is hopefully clear from the code that this formulation of the
classic puzzle uses four variables [C1,C2,C3,C4] to indicate
the carry digits. If we suppose that the user is only
interested in the behaviour of the program with respect to the primary
problem variables, which in this case corresponds to the variables
[S,E,N,D,M,O,R,Y], then we can annotate the program code by
declaring a viewable which contains these variables.
sendmore(Digits) :-
Digits = [S,E,N,D,M,O,R,Y],
Digits :: [0..9],
viewable_create(digits, Digits),
...
...
labeling(Carries),
labeling(Digits).
As can be seen, viewables are declared using the viewable_create/2 predicate, the first parameter of which is an atom which will be used to uniquely identify the viewable later, and the second argument is a (possibly nested) list of variables.
Declaring viewables has little performance overhead when running code normally (that is to say, without any visualisation clients), and so it is safe to leave the visualisation annotations in the code even when not visualising.
| S | E | N | D | |
| + | M | O | R | E |
| M | O | N | E | Y |
would be the array
and would be declared in the program as nested lists
viewable_create(equation,[[0, S, E, N, D], [0, M, O, R, E], [M, O, N, E, Y]]
or it could be declared in the program using ECLiPSe array syntax
viewable_create(equation,[]([](0, S, E, N, D),
[](0, M, O, R, E),
[](M, O, N, E, Y)))
Three points should be noted here,
So far we have introduced only static (or fixed dimension) viewables, but it is conceivable that during the course of a programs run, new variables may be introduced which the user has an interest in looking at. In order to accommodate this, viewables may be declared as having flexible dimensions.
To declare a viewable with flexible dimensions, the three argument
form of the viewable_create/3 predicate is used. The third
argument specifies the type of the viewable and at present the type
must be of the form array(FixityList, ElementType) where
fixed or
flexible specifying the fixity for each dimension. The fixity
denotes whether the dimension's size is fixed or may vary during the
time when the viewable is existent.
Let us expand our example by assuming that, during the program run our user is only interested in the digit variables but once labelling has finished they wish to also see the carry variables. Clearly the user is free to simply print out the carry variables after completing the labelling, but within the visualisation framework they may also expand the viewable by adding the carry digits to it. The code to do this is
sendmore(Digits) :-
Digits = [S,E,N,D,M,O,R,Y],
Digits :: [0..9],
viewable_create(equation,
[]([](0, S, E, N, D),
[](0, M, O, R, E),
[](M, O, N, E, Y)),
array([flexible,fixed], any)),
...
...
labeling(Carries),
labeling(Digits),
viewable_expand(equation, 1, [C1, C2, C3, C4, 0]).
Once declared as flexible, dimensions may be expanded by the viewable_expand/3 predicate. The predicate specifies which dimension to expand and which values should be added. Had the viewable been 3 dimensional, then the values to be added would need to be 2 dimensional. In general for an N dimensional viewable, when expanding a flexible dimension the values to be added must be N-1 dimensional arrays or nested lists.
As with viewable_create/2 and viewable_create/3, viewable_expand/3 silently succeeds with little overhead at runtime, so it too may be left in code even when not visualising.
As mentioned briefly in the previous section, viewables have a type definition which determines what sort of values may be stored in them. This type information allows visualisation clients to render the values in a fitting manner.
Explicitly stating that the variables in a viewable are
numeric_bounds where known will increase the number
of ways in which the
viewable elements may be viewed.
Each position in a viewable's dimension has an associated name. By
default, these names are simply the increasing natural numbers
starting from ``1''. So, for example, in the previous code samples
the variable R would be at location ["2","4"].
By using the most expressive form, the viewable_create/4 predicate allows the user to assign their own symbolic names to dimension locations.
In our on going example, we could name the first dimension positions
["send", "more", "money"] and the second dimension positions
["ten thousands", "thousands", "hundreds", "tens", "units"].
A version of viewable_expand/4 exists also which allows the user to assign a name to the new position of an expanded dimension.
Our completed example now looks like this
:-lib(viewable).
sendmore(Digits) :-
Digits = [S,E,N,D,M,O,R,Y],
Digits :: [0..9],
viewable_create(equation,
[]([](0, S, E, N, D),
[](0, M, O, R, E),
[](M, O, N, E, Y)),
array([flexible,fixed], numeric_bounds),
[["send", "more", "money"],
["ten thousands", "thousands",
"hundreds", "tens", "units"]]),
Carries = [C1,C2,C3,C4],
Carries :: [0..1],
alldifferent(Digits),
S #\= 0,
M #\= 0,
C1 #= M,
C2 + S + M #= O + 10*C1,
C3 + E + O #= N + 10*C2,
C4 + N + R #= E + 10*C3,
D + E #= Y + 10*C4,
labeling(Carries),
labeling(Digits),
viewable_expand(equation, 1, [C1, C2, C3, C4, 0], "carries").