为了澄清我的事件驱动的含义,我指的是一个我有
def onTrade(...)
每当某只股票交易时都会调用它。假设我想跟踪每日最高交易价格。对我来说,显而易见的解决方案是:
var dailyHigh = 0
def onTrade(...) {
if (price > dailyHigh) dailyHigh = price
}
有没有一种方法可以使用val来实现这个功能,而不是var?还要假设我将来可能想添加dailyLow、volumeHigh、volumeLow等。
为了澄清我的事件驱动的含义,我指的是一个我有
def onTrade(...)
每当某只股票交易时都会调用它。假设我想跟踪每日最高交易价格。对我来说,显而易见的解决方案是:
var dailyHigh = 0
def onTrade(...) {
if (price > dailyHigh) dailyHigh = price
}
有没有一种方法可以使用val来实现这个功能,而不是var?还要假设我将来可能想添加dailyLow、volumeHigh、volumeLow等。
这篇弃用观察者模式的论文可能会引起您的兴趣,但我认为它所描述的库尚未可用。
其实没有太大问题。一个完整的解决方案可能会使用Reader、IO和State单子,以及Iteratee和lenses,但这里有一个更简单的版本:
case class State(dailyHigh: Int = 0)
object Main {
type Event = (State => State)
def mainLoop(currState: State, events: Stream[Event]): State =
if (events.nonEmpty) {
val newState = events.head(currState)
mainLoop(newState, events.tail)
} else currState
def onTrade(price: Int): Event = (s: State) =>
if (price > s.dailyHigh) s.copy(dailyHigh = price) else s
def main(args: Array[String]) {
val events = onTrade(5) #:: onTrade(2) #:: onTrade(10) #:: onTrade(5) #:: Stream.empty
val finalState = mainLoop(State(), events)
println(finalState)
}
}
看,妈妈,没有变量!
状态可能会变得非常复杂,但这就是镜头的用武之地。使用镜头,很容易查询和更改(使用新值进行复制)任意复杂的数据结构。
对于事件来说,使用迭代器是自然而然的——从某种意义上讲,“onTrade”成为一个迭代器,由枚举器(“生成”事件的东西)在每个事件中调用。如果组合自部分函数,则可以将所有函数折叠到单个部分函数中。
另外,状态单子可以与IO单子结合在for-comprehensions。
最后,还有延续的选择。如果某些处理需要接收一系列事件,则每个事件的结果可以是延续,并且延续本身成为状态的一部分。
class Auction(seller: Actor, minBid: Int, closing: Date) extends Actor {
val timeToShutdown = 36000000 // msec
val bidIncrement = 10
def act() {
var maxBid = minBid - bidIncrement
var maxBidder: Actor = null
var running = true
while (running) {
receiveWithin((closing.getTime() - new Date().getTime())) {
case Offer(bid, client) =>
if (bid >= maxBid + bidIncrement) {
if (maxBid >= minBid) maxBidder ! BeatenOffer(bid)
maxBid = bid; maxBidder = client; client ! BestOffer
} else {
client ! BeatenOffer(maxBid)
}
case Inquire(client) =>
client ! Status(maxBid, closing)
case TIMEOUT =>
if (maxBid >= minBid) {
val reply = AuctionConcluded(seller, maxBidder)
maxBidder ! reply; seller ! reply
} else {
seller ! AuctionFailed
}
receiveWithin(timeToShutdown) {
case Offer(_, client) => client ! AuctionOver
case TIMEOUT => running = false
}
}
}
}
}
从未真正这样做过,但是你可以在流中创建新实例,而不是修改值。
其他进程可以迭代该流,在到达流的最后一个实例元素时等待。
我强烈推荐使用函数响应式编程来完成这项任务。这里有一个关于Scala中这种库的讲座:http://skillsmatter.com/podcast/scala/reactors