10

为了澄清我所说的事件驱动的意思,我指的是我有

def onTrade(...)

每次特定股票交易时都会调用它。假设我想跟踪每日最高交易价格。对我来说,显而易见的解决方案是:

var dailyHigh = 0

def onTrade(...) {
    if (price > dailyHigh) dailyHigh = price
}

有没有办法使用 val 而不是 var 来实现这个功能?还假设我将来可能想添加 dailyLow、volumeHigh、volumeLow 等。

4

5 回答 5

9

论文弃用观察者模式可能很有趣,但我相信它描述的库尚不可用。

于 2011-07-25T11:14:56.260 回答
8

其实问题不大。一个完整的解决方案可能会使用 Reader、IO 和 State monads 以及 Iteratee 和 lens,但这里有一个更简单的版本:

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

看,妈,没有 vars!

当然,状态可能会变得非常复杂,但这就是镜头的用武之地。使用镜头,很容易查阅和更改(使用新值复制)任意复杂的数据结构。

对事件使用 iteratee 是很自然的——在非常简单的意义上,“onTrade”变成了一个迭代器,如果每个事件由部分函数组成,则“onTrade”成为一个由枚举器(“生成”事件的东西)调用的迭代器,你可以折叠所有将它们合并为一个偏函数。

或者,State monad 可以与 IO monads 结合起来进行理解。

最后,还有延续的选项。如果某些处理需要接收一系列事件,那么每个事件的结果可以是一个延续,而延续本身成为状态的一部分。

于 2011-07-25T21:38:43.917 回答
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
               }
         }
      }
   }
}
于 2011-07-25T11:02:27.527 回答
0

从未真正做过,但您可以在 Stream 中创建新实例,而不是修改值。

然后其他进程可以迭代该 Stream,这将使它们在到达流的最后一个实例化元素时等待。

于 2011-07-25T09:59:59.513 回答
0

我强烈推荐该任务的函数式反应式编程。这是关于 scala 中这样一个库的讨论:http: //skillsmatter.com/podcast/scala/reactors

于 2011-07-25T11:24:51.187 回答