<< Tutorial 1 ^^Contents Tutorial 3 >>

Yield Prolog Tutorial

2. Variable and "Cut"

In Tutorial 2, we begin working with the actual classes used by Yield Prolog. The example code is in the distribution directory:

Recap

Let's repeat the "brother" example from Tutorial 1 as a Yield Prolog program.

Python
C#
import sys
# Hack sys.path for the examples.
sys.path.append("../..")
from YP import *
from Variable import *

def brother(Person, Brother):
for l1 in YP.unify(Person, "Hillary"):
for l2 in YP.unify(Brother, "Tony"):
yield False
for l2 in YP.unify(Brother, "Hugh"):
yield False
for l1 in YP.unify(Person, "Bill"):
for l2 in YP.unify(Brother, "Roger"):
yield False

def main():
print "Find relations:"
Brother = Variable()
for l1 in brother("Hillary", Brother):
print("Hillary has brother " +
Brother.getValue() + ".")

using System;
using System.Collections.Generic;
using YieldProlog;
class Tutorial2
{
static IEnumerable<bool> brother
(object Person, object Brother) {
foreach (bool l1 in YP.unify(Person, "Hillary")) {
foreach (bool l2 in YP.unify(Brother, "Tony"))
yield return false;
foreach (bool l2 in YP.unify(Brother, "Hugh"))
yield return false;
}
foreach (bool l1 in YP.unify(Person, "Bill")) {
foreach (bool l2 in YP.unify(Brother, "Roger"))
yield return false;
}
}

static void Main(string[] args) {
Console.WriteLine("Find relations:");
Variable Brother = new Variable();
foreach (bool l1 in brother("Hillary", Brother))
Console.WriteLine("Hillary has brother " +
Brother.getValue() + ".");
}
}

There are a few differences from Tutorial 1:
The UnifyingVariable class from Tutorial 1 let us make working Prolog programs, but we now discuss two important features added by the full Variable class: "variable chains" and "cutting" out of a for...in loop.

Variable chains

In UnifyingVariable.unify from Tutorial 1, what if the argument value is another UnifyingVariable? If we bind a variable to another variable (which may be bound to yet another variable), this creates a "variable chain". How can we handle this?

This is actually a "good problem" because variable chains allow a useful programming technique, as we'll see. And we can easily handle this as follows. For the full Variable class, we make the _value private and you access it through the getValue function. If the Variable is unbound, getValue returns the Variable object (for the same reasons we did this in the general getValue function). If the Variable is bound to a non-Variableobject (like a string), just return the object. Otherwise, it is bound to another Variable, so keep following the "variable chain". In this way, getValue() either returns the unbound Variable at the end of the variable chain or the value that the end Variable is bound to.

To see why a variable chain is useful, consider squaredRectangle:

import sys
# Hack sys.path for the examples.
sys.path.append("../..")
from YP import *
from Variable import *

def squaredRectangle(Width, Height):
for l1 in YP.unify(Width, Height):
yield False

def main():
print("Check if it is square:")
for l1 in squaredRectangle(10, 10):
print("10 by 10 rectangle is square.")

print("Make it square:")
Width = Variable()
Height = Variable()
for l1 in Width.unify(10):
for l2 in squaredRectangle(Width, Height):
print("A square of width " +
str(Width.getValue()) + " has height " +
str(Height.getValue()) + ".")

using System;
using System.Collections.Generic;
using YieldProlog;
class Tutorial2
{
static IEnumerable<bool> squaredRectangle
(object Width, object Height) {
foreach (bool l1 in YP.unify(Width, Height))
yield return false;
}

static void Main(string[] args) {
Console.WriteLine("Check if it is square:");
foreach (bool l1 in squaredRectangle(10, 10))
Console.WriteLine("10 by 10 rectangle is square.");

Console.WriteLine("Make it square:");
Variable Width = new Variable();
Variable Height = new Variable();
foreach (bool l1 in Width.unify(10)) {
foreach (bool l2 in squaredRectangle(Width, Height))
Console.WriteLine("A square of width " +
Width.getValue() + " has height " +
Height.getValue() + ".");
}
}
}

The simple squaredRectangle function just unifies Width and Height, which means that a rectangle is square if it has the same width and height.

As with most Prolog functions, this can be used in many ways. In the main function we first call squaredRectangle(10, 10), which succeeds because the Width and Height are the same. Next we bind the variable Width to 10 and call squaredRectangle(Width, Height, which binds Height to 10. These print:
Check if it is square:
10 by 10 rectangle is square.
Make it square:
A square of width 10 has height 10.
But what happens if we call squaredRectangle first, before we bind Width?

import sys
# Hack sys.path for the examples.
sys.path.append("../..")
from YP import *
from Variable import *

def main():
print("Make it square before we know the width:")
Width = Variable()
Height = Variable()
for l1 in squaredRectangle(Width, Height):
for l2 in Width.unify(10):
print("A square of width " +
str(Width.getValue()) + " has height " +
str(Height.getValue()) + ".")

using System;
using System.Collections.Generic;
using YieldProlog;
class Tutorial2
{
static void Main(string[] args) {
Console.WriteLine
("Make it square before we know the width:");
Variable Width = new Variable();
Variable Height = new Variable();
foreach (bool l1 in
squaredRectangle(Width, Height)) {
foreach (bool l2 in Width.unify(10))
Console.WriteLine("A square of width " +
Width.getValue() + " has height " +
Height.getValue() + ".");
}
}
}

Similar to before, this prints:
Make it square before we know the width:
A square of width 10 has height 10.
But how can this be? When we first call squaredRectangle, Width is still unbound, so how does it know to bind Height to 10? It doesn't. Instead, it binds Width to Height while Height is still an unbound variable, in a "variable chain". Next, how can Width.unify(10) bind Width to 10, because it is already bound to Height? Again, it doesn't. Width.unify follows the variable chain to find the unbound Height variable and binds that to 10. Finally, when we want the value from Width.getValue(), this follows the variable chain to find that Height is bound to 10 and returns that.

Looking at the details may be a little confusing, but the concept is this: unify says that any two things are equal. Even if they are two unbound variables, unify makes sure that if one of them is ever bound to a value, the other one will appear to be bound to the same value. This is useful, especially when you are dealing with structures that have lots of variables in them. You might figure out that parts of two different structures are the same, even though you don't know what the exact values are yet. Later, if you bind the variables in one of the structures, the other structure will appear to have the same values in the parts that you said are the same.

"Cutting" out of a loop

When we call a function like brother, it yields all matches that unify with the arguments. But what if we want a function called anyBrother that only yields the first match? We can break out of the for...in loop like this:

import sys
# Hack sys.path for the examples.
sys.path.append("../..")
from YP import *
from Variable import *

def anyBrother(Person, Brother):
for l1 in brother(Person, Brother):
yield False
break

def main():
print("Get one match:")
Brother = Variable()
for l1 in anyBrother("Hillary", Brother):
print("Hillary has a brother " +
Brother.getValue() + ".")
for l1 in anyBrother("Bill", Brother):
print("Bill has a brother " +
Brother.getValue() + ".")

using System;
using System.Collections.Generic;
using YieldProlog;
class Tutorial2
{
static IEnumerable<bool> anyBrother
(object Person, object Brother) {
foreach (bool l1 in brother(Person, Brother)) {
yield return false;
break;
}
}

static void Main(string[] args) {
Console.WriteLine("Get one match:");
Variable Brother = new Variable();
foreach (bool l1 in anyBrother("Hillary", Brother))
Console.WriteLine("Hillary has a brother " +
Brother.getValue() + ".");
foreach (bool l1 in anyBrother("Bill", Brother))
Console.WriteLine("Bill has a brother " +
Brother.getValue() + ".");
}
}

In anyBrother, as soon as it gets the first result, it breaks out of the loop so that it only yields once. As expected, this prints:
Get one match:
Hillary has a brother Tony.
Bill has a brother Roger.
Often, you only want to know if one solution exists, so in Prolog you can "cut" [1] out of a loop like this, which is more efficient.

Using "cut" for negation

Also, we can use "cut" to do negation. Here is a function noBrother which succeeds if Person has no brother:

import sys
# Hack sys.path for the examples.
sys.path.append("../..")
from YP import *
from Variable import *

def noBrother(Person):
Brother = Variable()
for l1 in brother(Person, Brother):
return
yield False

def main():
print("Use cut for negation:")
for l1 in noBrother("Hillary"):
print("Hillary has no brother.")
for l1 in noBrother("Chelsea"):
print("Chelsea has no brother.")

using System;
using System.Collections.Generic;
using YieldProlog;
class Tutorial2
{
static IEnumerable<bool> noBrother(object Person) {
Variable Brother = new Variable();
foreach (bool l1 in brother(Person, Brother))
yield break;
yield return false;
}

static void Main(string[] args) {
Console.WriteLine("Use cut for negation:");
foreach (bool l1 in noBrother("Hillary"))
Console.WriteLine("Hillary has no brother.");
foreach (bool l1 in noBrother("Chelsea"))
Console.WriteLine("Chelsea has no brother.");
}
}

In noBrother, if we get a first match for brother(Person, Brother) for any Brother, then cut out of the whole noBrother function without yielding. Otherwise, if the loop finishes without matching any brother, then yield once, meaning that there was "no brother" [2]. In the main function, we first call noBrother("Hillary"), but "Hillary" does match a brother, so noBrother does not succeed. Next, we call noBrother("Chelsea"), and since brother does not match any brother for "Chelsea", noBrother yields once so that we print "Chelsea has no brother":
Use cut for negation:
Chelsea has no brother.
In Tutorial 3, we work with lists.


1. I keep saying "cut" because this is what Prolog calls the "!" operator. Here is the equivalent Prolog code for anyBrother:
anyBrother(Person, Brother) :-
brother(Person, Brother), !.
You may remember that Variable needs to unbind the values before exiting the loop. So if we just break out of the loop, how does it do this? Inside the unify function, we use a try...finally block to unbind the variable:

    if not self._isBound:
self._value = YP.getValue(arg)
self._isBound = True
try:
yield False
finally:
# Remove the binding.
self._isBound = False

    if (!_isBound) {
_value = YP.getValue(arg);
_isBound = true;
try {
yield return false;
}
finally {
// Remove the binding.
_isBound = false;
}
}

So, if you break out of a loop, the compiler is smart enough to call the finally block when closing the iterator, and unbinds the variable as expected.

2. The equivalent Prolog code is:
noBrother(Person) :-
brother(Person, _Brother), !, fail.
noBrother(_Person).

noBrother('Hillary'), write('Hillary has no brother.'), nl.
noBrother('Chelsea'), write('Chelsea has no brother.'), nl.
There are other equivalent ways to write noBrother, such as:
noBrother1(Person) :-
(brother(Person, _Brother) -> fail ; true).
but the -> operator translates into the pattern shown in noBrother which makes it clear how to do an "if...then...else". Of course, you can also write:
noBrother2(Person) :- 
  \+ brother(Person, _Brother).
but you can think of the \+ operator as a simple "if...then...else" where "then" fails and "else" succeeds.