To enhance my learnings and to write some more code, Now I implemented the constraint propagation network from SICP section-3.3.5 too.
Here is the code...
//all the Constraint implementations
//extend the Constraint class
abstract class Constraint {
def informAboutValue()
def informAboutNoValue()
}
//Connector implementation
class Connector {
private var myName = "Unknown"
private var myVal:Int = _
private var informant:AnyRef = _
private var constraints:List[Constraint] = List()
def this(newName:String) {
this()
myName = newName
}
def name = myName
def hasValue() = informant != null
def value = {
if(!hasValue())
throw new RuntimeException("""|This connector
| does not have a
| value.""".stripMargin)
myVal
}
def setValue(newVal:Int, informer:AnyRef) {
Tuple(hasValue(),newVal == myVal) match {
case Tuple2(false, _) => {
myVal = newVal
informant = informer
constraints.foreach(
(c:Constraint) =>
{ if(c ne informer) c.informAboutValue()})
}
case Tuple2(true,true) => ; //ignore
case Tuple2(true,false) =>
throw new RuntimeException("Contradiction " +
newVal + " " + myVal)
}
}
def forgetValue(retractor:AnyRef) {
if(retractor eq informant) {
informant = null
constraints.foreach(
(c:Constraint) =>
{ if(c ne retractor) c.informAboutNoValue() })
}
}
def connect(c:Constraint) {
constraints = c :: constraints
}
}
//===== Constraints =====
case class Adder(a:Connector,b:Connector,
c:Connector) extends Constraint {
a.connect(this)
b.connect(this)
c.connect(this)
def informAboutValue() {
Tuple(a.hasValue(),b.hasValue(),c.hasValue())
match {
case Tuple3(true,true,false) =>
c.setValue(a.value + b.value, this)
case Tuple3(true,false,true) =>
b.setValue(c.value - a.value, this)
case Tuple3(false,true,true) =>
a.setValue(c.value - b.value, this)
case _ => ; //ignore
}
}
def informAboutNoValue() {
a.forgetValue(this)
b.forgetValue(this)
c.forgetValue(this)
informAboutValue()
}
}
case class Multiplier(a:Connector,b:Connector,
c:Connector) extends Constraint {
a.connect(this)
b.connect(this)
c.connect(this)
def informAboutValue() {
Tuple(a.hasValue(),b.hasValue(),
(a.hasValue() && a.value == 0) ||
(b.hasValue() && b.value == 0),
c.hasValue())
match {
case Tuple4(_,_,true,_) =>
c.setValue(0, this)
case Tuple4(true,true,_,false) =>
c.setValue(a.value * b.value, this)
case Tuple4(true,false,_,true) =>
b.setValue(c.value / a.value, this)
case Tuple4(false,true,_,true) =>
a.setValue(c.value / b.value, this)
case _ => ; //ignore
}
}
def informAboutNoValue() {
a.forgetValue(this)
b.forgetValue(this)
c.forgetValue(this)
informAboutValue()
}
}
case class Constant(value:Int,
c:Connector) extends Constraint {
c.connect(this)
c.setValue(value, this)
def informAboutValue() {
throw new RuntimeException("""|CONSTANT constraint,
| request not allowed
|.""".stripMargin) }
def informAboutNoValue() {
throw new RuntimeException("""|CONSTANT constraint,
| request not allowed
|.""".stripMargin) }
}
case class Probe(c:Connector) extends Constraint {
c.connect(this)
def informAboutValue() {
printProbe(c.value.toString()) }
def informAboutNoValue() {
printProbe("?") }
private def printProbe(value:String) {
println("Probe: " + c.name + " = " + value)
}
}
//====== simulation =====
def celsiusFahrenheitConverter(c:Connector,f:Connector) {
val u = new Connector()
val v = new Connector()
val w = new Connector()
val x = new Connector()
val y = new Connector()
Multiplier(c, w, u)
Multiplier(v, x, u)
Adder(v, y, f)
Constant(9, w)
Constant(5, x)
Constant(32, y)
}
val C = new Connector("Celsius Temp")
Probe(C)
val F = new Connector("Fahrenheit Temp")
Probe(F)
celsiusFahrenheitConverter(C,F)
C.setValue(25, 'user)
//Probe: Fahrenheit Temp = 77
//Probe: Celsius Temp = 25
F.setValue(212, 'user)
//java.lang.RuntimeException: Contradiction 212 77
C.forgetValue('user)
//Probe: Fahrenheit Temp = ?
//Probe: Celsius Temp = ?
F.setValue(212, 'user)
//Probe: Celsius Temp = 100
//Probe: Fahrenheit Temp = 212
No comments:
Post a Comment