为了澄清事件驱动我的意思,我指的是我有
的情况def onTrade(...)
每次特定股票交易时都会调用。假设我想跟踪每日最高交易价格。对我来说,显而易见的解决方案是:
var dailyHigh = 0
def onTrade(...) {
if (price > dailyHigh) dailyHigh = price
}
有没有办法用val而不是var来实现这个功能?还假设我可能希望将来添加dailyLow,volumeHigh,volumeLow等。
答案 0 :(得分:9)
论文Deprecating the observer pattern可能很有意思,但我相信它所描述的图书馆还没有。
答案 1 :(得分:7)
实际上并不是什么大问题。完整的解决方案可能会使用Reader,IO和State monads以及Iteratee和镜头,但这里有一个更简单的版本:
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)
}
}
看,妈,没有变种!
当然,状态可能变得相当复杂,但这就是镜头的用武之地。使用镜头,可以很容易地查询和更改(复制新值)任意复杂的数据结构。使用iteratees对于事件来说很自然 - 在一个非常简单的意义上,“onTrade”成为一个由枚举器调用的迭代器(“生成”事件的东西),如果由部分函数组成,你可以将它们全部折叠成一个部分函数。
或者,状态monad可以与IO monads组合以进行理解。
最后,还有继续选项。如果某些处理需要接收一系列事件,那么每个事件的结果可以是一个延续,并且延续本身也会成为该状态的一部分。
答案 2 :(得分:2)
有时自然需要可变状态,以下是“scala by example”一书中的示例。
它也有一些可变状态(maxBid,maxBidder)。因此var并不总是坏主意。有时它工作正常。
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
}
}
}
}
}
答案 3 :(得分:0)
从未实际完成过,但您可以在Stream中创建新实例,而不是修改值。
然后,其他进程可以迭代该流,这会使它们在到达流的最后一个实例化元素时等待。
答案 4 :(得分:0)
我强烈推荐针对该任务的功能性反应式编程。以下是scala中关于这样一个库的讨论:http://skillsmatter.com/podcast/scala/reactors