ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 02.01.2026

Просмотров: 3507

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Section 18.4

Chapter 18 · Stateful Objects

407

val a = new Wire val b = new Wire val c = new Wire

or, equivalent but shorter, like this:

val a, b, c = new Wire

Second, there are three procedures which “make” the basic gates we need:

def inverter(input: Wire, output: Wire)

def andGate(a1: Wire, a2: Wire, output: Wire) def orGate(o1: Wire, o2: Wire, output: Wire)

What’s unusual, given the functional emphasis of Scala, is that these procedures construct the gates as a side-effect, instead of returning the constructed gates as a result. For instance, an invocation of inverter(a, b) places an inverter between the wires a and b. It turns out that this side-effecting construction makes it easier to construct complicated circuits gradually. Also, although methods most often have verb names, these have noun names that indicate which gate they are making. This reflects the declarative nature of the DSL: it should describe a circuit, not the actions of making one.

More complicated function boxes can be built from the basic gates. For instance, the method shown in Listing 18.6 constructs a half-adder. The halfAdder method takes two inputs, a and b, and produces a sum, s, defined by “s = (a + b) % 2” and a carry, c, defined by “c = (a + b) / 2”. A diagram of the half-adder is shown in Figure 18.2.

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)

}

Listing 18.6 · The halfAdder method.

Note that halfAdder is a parameterized function box just like the three methods that construct the primitive gates. You can use the halfAdder

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index


Section 18.4

Chapter 18 · Stateful Objects

408

a

d

 

 

e

s

b

 

c

 

 

Figure 18.2 · A half-adder circuit.

method to construct more complicated circuits. For instance, Listing 18.7 defines a full, one-bit adder, shown in Figure 18.3, which takes two inputs, a and b, as well as a carry-in, cin, and which produces a sum output defined by “sum = (a + b + cin) % 2” and a carry-out output defined by “cout = (a + b + cin) / 2”.

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)

}

Listing 18.7 · The fullAdder method.

Class Wire and functions inverter, andGate, and orGate represent a little language with which users can define digital circuits. It’s a good example of an internal DSL, a domain specific language defined as a library in a host language instead of being implemented on its own.

The implementation of the circuit DSL still needs to be worked out. Since the purpose of defining a circuit in the DSL is simulating the circuit, it makes sense to base the DSL implementation on a general API for discrete event simulation. The next two sections will present first the simulation API and then the implementation of the circuit DSL on top of it.

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index



Section 18.5

Chapter 18 · Stateful Objects

409

 

 

 

 

b

 

 

 

 

 

 

 

sum

 

 

 

half

 

 

 

 

 

 

 

 

 

 

 

 

 

s

c2

a

 

 

 

adder

 

 

half

 

 

 

 

 

 

 

cout

 

 

 

 

 

 

 

 

cin

 

 

adder

c1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 18.3 · A full-adder circuit.

18.5 The Simulation API

The simulation API is shown in Listing 18.8. It consists of class Simulation in package org.stairwaybook.simulation. Concrete simulation libraries inherit this class and augment it with domain-specific functionality. The elements of the Simulation class are presented in this section.

A discrete event simulation performs user-defined actions at specified times. The actions, which are defined by concrete simulation subclasses, all share a common type:

type Action = () => Unit

This statement defines Action to be an alias of the type of procedure that takes an empty parameter list and returns Unit. Action is a type member of class Simulation. You can think of it as a more readable name for type () => Unit. Type members will be described in detail in Section 20.6.

The time at which an action is performed is simulated time; it has nothing to do with the actual “wall clock” time. Simulated times are represented simply as integers. The current simulated time is kept in a private variable:

private var curtime: Int = 0

The variable has a public accessor method, which retrieves the current time:

def currentTime: Int = curtime

This combination of private variable with public accessor is used to make sure that the current time cannot be modified outside the Simulation class. After all, you don’t usually want your simulation objects to manipulate the current time, except possibly if your simulation models time travel.

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index


Section 18.5

Chapter 18 · Stateful Objects

410

abstract class Simulation {

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) {

val item = WorkItem(currentTime + delay, () => block) 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()

}

}

Listing 18.8 · The Simulation class.

Cover · Overview · Contents · Discuss · Suggest · Glossary · Index