在Scala中,我如何将事件驱动编程与函数方法结合起来?

在Scala中,我如何将事件驱动编程与函数方法结合起来?,scala,event-driven,Scala,Event Driven,为了澄清我所说的事件驱动的意思,我指的是我 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等。

实际上从未这样做过,但您可以在流中创建新实例,而不是修改值


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

有时自然需要可变状态,下面是《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
               }
         }
      }
   }
}

这篇论文可能会引起人们的兴趣,但我相信它所描述的图书馆目前还没有

我强烈建议对该任务进行功能性反应式编程。下面是关于scala中这样一个库的讨论:

事实上,这不是什么大问题。完整的解决方案可能会使用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

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

对事件使用迭代器是很自然的——从非常简单的意义上说,“onTrade”成为一个迭代器,由枚举器(“生成”事件)调用每个事件。如果由分部函数组成,则可以将所有事件折叠成一个分部函数

或者,状态单子可以与上的IO单子组合以进行理解


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

也请检查这一点,这基本上是相同的解决方案,这是Jens Schauder的建议。我只是对函数式编程有了初步了解,并试图学习其中的诀窍(我的历史是程序化的,然后是面向对象的)。我很难理解为什么(或者是否)这种方法“更好”。我确实理解函数式编程中不变性的基本原因,但这似乎要复杂得多!这仅仅是因为我是新手吗?@MPT Newness肯定扮演了一个角色——可能是一个重要的角色。但这不仅仅是。。。这些东西是抽象的,因此,它们统一了许多在不习惯它的程序员的头脑中不同的东西,并隐藏了细节。处理抽象概念比处理具体事物更难。为了更好,它使程序更易于组合和测试。但当你看一些小例子时,它确实显得不合适。@Didier我用这个链接得到了一个404