Scala - Stateful objects



You can observe the principle difference between a purely functional object and a stateful one even without looking object's implementation. 

like the list declaration, which will always return 'a' as the List.head()...while for a stateful object,  on the other hand, the resulkt of a method call or field access  may depend on what operation  were previously performed on the object.. A good example of a stateful object is a bank account, list show a simplified implementation of bank accounts. 

class BankAccount { 
  private var bal : Int = 0
  def balance : Int = bal
  
  def deposit(amount : Int) { 
    require(amount > 0)
    bal += amount
  }
  
  def withDraw(amount : Int) : Boolean = 
    if (amount > bal ) false
    else { 
      bal -= amount
      true
    }
  
}
and with the implementaion, you may run the following code to test the state operation. 
val account = new BankAccount
account deposit 100
account withdraw 80
account withdraw 80
the first withdraw return a 'true' value while the second returns a 'false' value. 

Reassignable variables and properties


you can perform two fundamental operation on a reassignable variable: get the value or set it to a new value. 

In scala, every var that is  a non-private member of some object implicitly defineds a getter and a setter method with it. 

unlike the java, the getter and setter of a variable are different, the getter of a x is just named 'x', while its setter is named 'x_='.

so the following definition of Time classes


class Time { 
  var hour = 12
  var minute = 0
}


is the same as the folowing. 


// new version of Time with getter and Setter
class Time { 
  private[this] var h = 12
  private[this] var m = 0
  
  def hour : Int = h
  def minute : Int = m
  
  // the interesting part-  the setter is written as such, becareful of the _= that is appended to the end of methods 
  def minute_=(x :Int) { h = x}
  def hour_=(x : Int) { m = x}
}

and a even more robust one is like the one below. 


// new variant of Time with getter and Setter
class Time { 
  private[this] var h = 12
  private[this] var m = 0
  
  def hour : Int = h
  def minute : Int = m
  
  // the interesting part-  the setter is written as such, becareful of the _= that is appended to the end of methods 
  def minute_=(x :Int) { 
    require (x < 60 && 0 <= x)
    h = x
  }
  def hour_=(x : Int) { 
    require(0 <= x && x < 12)
    m = x
  }
}


while this is like the C# properties, the setter can ensure the invariant. and you can also log all access to the getter and setter of the variable. 

and you can also define a getter or setter without an associated field. An example is the following thermometer which encapsulate a temperatur variable that an be read and update. 
 the temperature can be expressed in Celsius or Fahrenheit degrees. The classes below allows you to get and set the temperatur in either measure. 

class Thermometer {
  var celsius : Float = _ // by specifying '_' as the "initializing value", of the variable
  def fahrenheit = celsius * 9 / 5 + 32
  def fahrenheit_= (f : Float) { 
    celsius = (f - 32) * 5 / 9 
  }
  override def toString = fahrenheit + "F/" + celsius + "C"
}
one special note on the = _ as the initializing value of celsius, which will contain the temperatur in degree celsius.. the celsius variable is initially set to a default value by specifying '_' as the "initializing value" of the variable. more precisely, it assigns a zero value to that field. The zero value depends on the field's type, it is 0 for numeric types, false for booleans, and null for references types, this is the same variable was defined in Java without an initializer. 


the test code is written as follow. 

val t= new Thermometer
t.celsius = 100
t
t.fahrenheit = -40
t

A case stydtm Discrete event simulation


this example is taken from the classic textblook "strucutre and Interpretation of Computer Programs" by Abelson and Sussman.. and instead of schema this example is using Scala. 

first, we will define the base classes for the simulation. 


// file: 
//   simulation.scala
// description:
//   simulation API

package org.stairwaybook.simulation

abstract class Simulation { 
  
  
  // type will define an alias of some types, 
  // the example below will define () => Unit as the type of "Action"
  type Action = () => Unit
  
  
  case class WorkItem(time :Int, action: Action)
  
  private var curtime = 0
  def currentTime : Int = curtime
  
  private var agenda : List[WorkItem] = List()
  
  private def insert(ag : List[WorkItem], item : WorkItem) : List[WorkItem] = { 
    if (ag.isEmpty || item.time < ag.head.time) item :: ag
    else ag.head :: insert(ag.tail, item)
  }
  
  def afterDelay(delay:Int)(block: => Unit) { // by-name argument 
    val item = WorkItem(currentTime + delay, () => block) // pass in lambda 
    agenda = insert (agenda, item)
  }
  
  private def next() { 
    (agenda: @unchecked) match { 
      case item :: rest => 
        agenda = rest
        curtime = item.time
        item.action()
    }
  }
  
  def run() { 
    afterDelay(0) { 
      println("*** simulation started, time = " + 
          currentTime +  " ***")
    }
    while (!agenda.isEmpty) next()
  }
}

the basic class has a list which will arrange the work items in the order of the action time, the insert method does the trick of inserting work items according to its order.


the action to be performed is passed with a by-name argument/paramter. and we havbe define a type alias, which is like a delegate in C#, the type alias is defined as follow. 


// type will define an alias of some types, 
  // the example below will define () => Unit as the type of "Action"
  type Action = () => Unit
and the afterDelay method will invoke by-name parameter, which is passed to the block parameter. an examle is 



afterDelay(delay) { count += 1 }
recall the declaration of the afterDelay method. 



def afterDelay(delay :Int) (block :=> Uint)
with the base class and the basic operation, the next thing to do is to extend it and implement the domain specific language for circuit.



// file: 
//   BasicCircuitSimulation.scala
// description:
//   simulation 

package org.stairwaybook.simulation

abstract class BasicCircuitSimulation extends Simulation {
  
  def InverterDelay : Int
  def AndGateDelay : Int
  def OrGateDelay : Int
  
  
  class Wire { 
    private var sigVal = false
    private var actions : List[Action] = List()
    
    def getSignal = sigVal 
    
    
    def setSignal(s : Boolean) = { 
       if (s != sigVal) {
         sigVal = s
         actions foreach (_ ()) // iterate through the collection and call each action one by one 
       }
    }
    
    def addAction(a : Action) = {
      actions = a :: actions
      a()
    }
  }
  
  def inverter(input : Wire, output : Wire) = { 
    def invertAction() {
      val inputSig = input.getSignal
      afterDelay(InverterDelay) {
        output setSignal !inputSig
      }
    }
    
    input addAction invertAction
  }
  
  def andGate(a1: Wire, a2: Wire, output : Wire) = {
    def andAction() = {
      val a1Sig = a1.getSignal
      val a2Sig = a2.getSignal
      afterDelay(AndGateDelay) { 
        output setSignal (a1Sig & a2Sig)
      }
    }
    a1 addAction andAction
    a2 addAction andAction
  }
  
  def orGate(o1: Wire, o2: Wire, output : Wire) = {
    def orAction() = {
      val o1Sig = o1.getSignal
      val o2Sig = o2.getSignal
      afterDelay(AndGateDelay) { 
        output setSignal (o1Sig | o2Sig)
      }
    }
    o1 addAction orAction
    o2 addAction orAction
  }
  
  // you can simulation the action of putting a probe on a wire..
  def prob(name: String, wire : Wire) = {
    def probeAction() { 
      println(name + " " + currentTime + " new-value = " + wire.getSignal)
    }
    wire addAction probeAction
  }
}


in the BasicSimulationCircuitSimulation we defines three abstract method, they are InverterDelay, AndGateDelay and OrGateDelay, the actual delay is not known at the level of this class.

note on the class above, there is such code. 


def setSignal(s : Boolean) = { 
       if (s != sigVal) {
         sigVal = s
         actions foreach (_ ()) // iterate through the collection and call each action one by one 
       }
    }
what does it mean here is that if a singal is set in the input, and the signal is different from its original value, then all action that is connected to this wire will be fired (actions foreach (_ () ) means for each of the actions in the list, assign a temporary value to _, and then invoke the action denoted by _;.


and let's take a close look at the inverter method. 


def inverter(input : Wire, output : Wire) = { 
    def invertAction() {
      val inputSig = input.getSignal
      afterDelay(InverterDelay) {
        output setSignal !inputSig
      }
    }
    
    input addAction invertAction
  }
what it means is 1. get the singla input from the input wire, and setSignal on the output wire the reverse result of inputSignal. You can apply the same analysis on the andGate and the orGate.


One last thing, in order to inspect changes of signals on wires, To accomplish this, you can simulate the action of putting a probe on a wire.

// you can simulation the action of putting a probe on a wire..
  def prob(name: String, wire : Wire) = {
    def probeAction() { 
      println(name + " " + currentTime + " new-value = " + wire.getSignal)
    }
    wire addAction probeAction
  }
To running the simulation, it's time to see the simulation in action. to define a concrete simulation, we need to inherit from the simulation framework class. to see something interesting, we will extend the BasicCircuitSimulation and contains method definitions for half-adder and full-adder. 
// file
//  circuitSimuation.scala
// description:
//  the circuit simulation 

package org.stairwaybook.simulation

abstract class CircuitSimulation extends BasicCircuitSimulation { 
  def halfAdder (a : Wire, b : Wire, s : Wire, c : Wire ) { 
    val d,e  = new Wire
    orGate(a, b, d)
    andGate(a, b, c)
    inverter(c, e)
    andGate(d, e, s)
  }
  
  def fullAdder(a : Wire, b : Wire, cin : Wire, sum : Wire, cout : Wire ) { 
    val s, c1, c2 = new Wire
    halfAdder(a, cin, s, c1)
    halfAdder(b, s, sum, c2)
    orGate(c1, c2, cout)
  }
}
the real simuation will define the concrete delays before it is useful. 


// file 
//   MySimulation.scala
// description:
//   my simulation scala 

// 
// the simulation object o8i
// object extends class ?


// runs the following in the repl 
package org.stairwaybook.simulation
// package org.stairwaybook.simulation

object MySimulation extends CircuitSimulation { 
  def InverterDelay = 1
  def AndGateDelay = 3
  def OrGateDelay = 5
}

import MySimulation._

val input1, input2, sum , carry = new Wire

prob("sum", sum)

prob("carry", carry)

halfAdder(input1, input2, sum, carry)

input1 setSignal true
run()

input2 setSignal true
run()



转载于:https://my.oschina.net/u/854138/blog/139400

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值