This lecture covers Generating Functions(a way to represent sequences), their usage in solving some counting problems and recurrance equations.
Here are my notes...



/* Notice the argument of actor method, its a
* no-arg(not empty arg list)
* function that returns Unit */
def actor(body: => Unit): Actor = {
/* Here java style syntax of anonymous classes is being used.
* we're creating an instance not of Actor class but a anonymous
* subclass that is implementing a method act() which is declared
* in trait Reactor(also its evident that its not mandatory to use
* keyword override when implementing something from the trait)
* and overriding scheduler. */
val a = new Actor {
def act() = body
override final val scheduler: IScheduler = parentScheduler
}
/* also see that actor is started right here. */
a.start()
a
}
scala> def A[a](b:a){ println(b) }
A: [a](a)Unit
scala> A[String]("hi")
hi
scala> A[String](2)
:6: error: type mismatch;
found : Int(2)
required: String
A[String](2)
^
def receive[R](f: PartialFunction[Any, R]):R
[n] | [# of pairs] | [pairs] |
2 - A,B | 1 | (A,B) |
3 - A,B,C | 3 | (A,B);(B,C);(C,A) |
4 - A,B,C,D | 6 | (A,B);(B,C);(C,D);(D,A);(A,C);(B,D) |
//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
//Segment and Agenda
import scala.collection.mutable.Queue
case class Segment(val time:Int,
val queue:Queue[()=>Unit]) extends Ordered[Segment] {
require(time >= 0)
override def compare(that:Segment) = {
if(that.time == time) 0
else if(that.time < time) -1
else 1
}
}
import scala.collection.mutable.PriorityQueue
class Agenda {
private var currentTime = 0
private val timeSegments = new PriorityQueue[Segment]()
def getCurrentTime = currentTime
def addItem(time:Int, item:()=>Unit) {
getTimeSegment(time) match {
case None =>
val queue = new Queue[()=>Unit]()
queue += item
timeSegments += Segment(time, queue)
case Some(segment) => segment.queue += item
}
}
def propagate():Unit = {
if(!timeSegments.isEmpty) {
firstItem()
propagate()
}
else println("Agenda Propagation Done.")
}
//gets first agenda item and removes it
//from the agenda
private def firstItem = {
val segment = timeSegments.max
currentTime = segment.time
val item = segment.queue.dequeue
if(segment.queue.isEmpty)
timeSegments.dequeue
item
}
//get the Segment matching time from timeSegments
private def getTimeSegment(time:Int) = {
val tmp = timeSegments.filter((s:Segment)=>s.time == time)
if(tmp.isEmpty) None else Some(tmp.first)
}
}
//Wire
class Wire() {
var signal = false
private var actions:List[()=>Unit] = List()
def setSignal(b:Boolean) {
if(b != signal) {
signal = b
//execute all actions
actions.foreach((proc:()=>Unit) => proc())
}
}
def addAction(proc:()=>Unit) {
actions = proc :: actions
proc()
}
}
//Probe
def probe(name:String, wire:Wire, agenda:Agenda) {
wire.addAction(
() =>
println(name + " " + agenda.getCurrentTime
+ " ,New Value = " + wire.signal))
}
//after-delay
def afterDelay(delay:Int, proc:() => Unit, agenda:Agenda) {
require(delay >= 0)
agenda.addItem(agenda.getCurrentTime + delay, proc)
}
//====================== SIMULATION ========================
val theAgenda = new Agenda()
val inverterDelay = 2
val andGateDelay = 3
val orGateDelay = 5
//inverter
def inverter(in:Wire, out:Wire) = {
in.addAction(
() => {
val newVal = !in.signal
afterDelay(inverterDelay, () =>
(out.setSignal(newVal)),theAgenda)
})
"Ok"
}
//and gate
def andGate(in1:Wire, in2:Wire, out:Wire) = {
val action = () => {
val newVal = in1.signal && in2.signal
afterDelay(andGateDelay,
() => (out.setSignal(newVal)),
theAgenda) }
in1.addAction(action)
in2.addAction(action)
"Ok"
}
//or gate
def orGate(in1:Wire, in2:Wire, out:Wire) = {
val action = () => {
val newVal = in1.signal || in2.signal
afterDelay(orGateDelay,
() => (out.setSignal(newVal)),
theAgenda) }
in1.addAction(action)
in2.addAction(action)
"Ok"
}
//half-adder
def halfAdder(in1:Wire, in2:Wire, sum:Wire, carry:Wire) = {
val t1 = new Wire()
val t2 = new Wire()
orGate(in1, in2, t1)
andGate(in1, in2, carry)
inverter(carry, t2)
andGate(t1, t2, sum)
"Ok"
}
// == Running the simulation ==
val in1 = new Wire()
val in2 = new Wire()
val sum = new Wire()
val carry = new Wire()
probe("sum",sum,theAgenda)
//sum 0 ,New Value = false
probe("carry",carry,theAgenda)
//carry 0 ,New Value = false
halfAdder(in1,in2,sum,carry)
//Ok
in1.setSignal(true)
theAgenda.propagate()
//sum 8 ,New Value = true
//Agenda Propagation Done.
in2.setSignal(true)
theAgenda.propagate()
//carry 11 ,New Value = true
//sum 16 ,New Value = false
//Agenda Propagation Done.