ASP-03 | Variables
Variables in ASP allow rules to express general patterns rather than just specific instances, enabling more powerful and reusable logic programs. Variables work as placeholders that match terms, expanding the expressivity of predicates.Photo Credit: Rob Grzywinski
Variables
The rule below uses the variable X:fruit(apple ; orange) .
has(X) ← fruit(X) .
A variable starts with an underscore (_) or upper case letter (A ... Z). Variables in ASP are universally quantified (∀) and are read as "for every". The rule in (1) is read as:- For every
X, if fruit(X) is in the answer set then has(X) is in the answer set; - For every
X, has(X) must be in the answer set if fruit(X) is in the answer set; - For every
X, if fruit(X) holds then has(X) holds.
If you are familiar with mathematical logic, the rule is equivalent to: ∀X fruit(X) ⊃ has(X).Unlike in imperative languages, variables are not named storage cells where values are stored. In Logic Programming, variables are placeholders that match every term. If fruit(apple) fruit(orange) then X in fruit(X) takes on the values apple and orange. The rule has(X) ← fruit(X) can now be instantiated for each of the values of X.fruit(apple ; orange) .
has(apple) ← fruit(apple) .
has(orange) ← fruit(orange) .
Grounding (substitution) now proceeds as before producing the results:{fruit(apple) fruit(orange)has(apple) has(orange)}
Visualizing Answer Sets
As the number of predicates and terms in an answer set grows, it can become overwhelming to understand the relationships just by looking at the raw output. A more intuitive way to visualize these relationships is through a tabular representation. Let's look at a tabular representation of our answer sets from (2):Each column represents a predicate (with its arity noted after the "/") and each row shows how terms align across predicates. This visualization makes it clear how the variable X in our rule has(X) ← fruit(X) creates a one-to-one mapping between terms in fruit/1 and has/1. When reading this visualization:- The predicates are shown as column headers
- Terms that match through variables are aligned horizontally
- The curly braces
{ } on the sides remind us we're working with sets
We'll use this style of visualization throughout to help illustrate how variables bind terms across predicates and how rules create relationships between terms.
Variable Scope
The scope of a variable is within a single statement and notice that the same variable is found on both sides of the arrow ("←"). In the ASP literature, X in (1) is called a global variable but it would be more apt to think of it as a statement variable since that is its scope. Statement (global) variables always produce new instances of a rule as demonstrated in the example above. This will become very important to remember in future sections when local (or expression) variables are introduced. Expression (local) variables do not produce new instances of a rule.In both the literature as well as in common programming, single letter variables are the norm. Much like loop counter variables in imperative programming, because the scope of variables in ASP is so small, there is typically no need to provide elaborate variable names.Anonymous Variables
Examine the facts belowon(book, shelf) .
on(computer, desk) .
on(cup, desk) .
How might we refer to all of the surfaces that objects are placed on? (The first term of on/2 is the object and the second term is what that object is placed on.)surface(S) ← on(_, S) .
The anonymous variable "_ " effectively means "match anything but ignore it". (Internally, the solver associates a named variable with each anonymous variable but does not return them in the answer set.)Remember that answer sets are sets so the result of (3) and (4) is .Similarly, one can easily refer to all of the objects that are sitting on surfaces:objects(O) ← on(O, _) .
which results in .Notice that the variables "S" (for surface) and "O" (for object) were used to provide a visual reminder as to what each term in the predicate refers to.It is also possible to retrieve only those objects that are on the desk:desk_objects(DO) ← on(DO, desk) .
which results in {| desk_objects/1 |
|---|
| computer |
| cup |
} . This technique comes up often in ASP programming and debugging.
Multiple Anonymous Variables
When using multiple anonymous variables in the same rule, it's important to understand that each "_" is treated as a distinct variable. Consider this example with student grades:grade(bob, math, 91) .
grade(bob, history, 87) .
grade(ann, math, 92) .
grade(ann, history, 100) .
If we want to find all students who got any grade (regardless of subject or score):student(S) ← grade(S, _, _) .
This results in where each "_" is treated as a separate variable — the first one matches any subject and the second matches any score.If we want to find students who got a perfect score in any subject:perfect(S) ← grade(S, _, 100) .
This results in .
Sets
One can define a variable in terms of a set of atoms:p(X) ← X = (a ; b) .
The "=" operator is read as "belongs to". The body of the rule is read as "X belongs to the set of atoms a and b" and the answer set is .You can always expand these sets into individual rules if you become disorientedp(X) ← X = a .
p(X) ← X = b .
The variable can be substituted for its value and simplified furtherp(a) .
p(b) .
When we look at intervals and arithmetic operators in the next section, we will expand on this topic.
Example: Genealogy
Multiple Variables
More than one variable can be usedmother(pete, mary) .
mother(zuzu, mary) .
father(pete, george) .
father(zuzu, george) .
parent(C, M) ← mother(C, M) .
parent(C, F) ← father(C, F) .
The facts relate the children pete and zuzu to their mother mary and their father george. The rules then generalize those relations into parent/2. The result is {| parent/2 |
|---|
| pete | george |
| pete | mary |
| zuzu | george |
| zuzu | mary |
} . The variable names are an attempt to remind the reader which term is the child (C) and which term is parent — either mother (M) or father (F).Asking Questions of Logic Programs
One of the most interesting and exciting aspects of Logic Programing is being able to "ask questions" of the program using variables. Looking at our genealogy example (13) we can ask: who are the parents of pete?answer(P) ← parent(pete, P) .
Or: who are the children of mary?answer(C) ← parent(C, mary) .
There's nothing special about the predicate answer/1. Any predicate with sufficient arity to retrieve the desired values will suffice.