Как в Scala я бы совмещал программирование на основе событий с функциональным подходом? - PullRequest
9 голосов
/ 25 июля 2011

Чтобы уточнить, что я имею в виду под управлением событиями, я имею в виду ситуацию, в которой у меня есть

def onTrade(...)

, которая вызывается каждый раз, когда совершается торговля конкретной акцией.Предположим, я хочу отслеживать дневную максимальную цену сделки.Для меня очевидное решение:

var dailyHigh = 0

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

Есть ли способ достичь этой функциональности, используя val вместо var?Предположим также, что в будущем я могу захотеть добавить dailyLow, volumeHigh, volumeLow и т. Д.

Ответы [ 5 ]

9 голосов
/ 25 июля 2011

Документ Устаревший шаблон наблюдателя может представлять интерес, но я полагаю, что библиотека, которую он описывает, еще не доступна.

7 голосов
/ 26 июля 2011

Не большая проблема, на самом деле. Полное решение, вероятно, будет использовать Reader, IO и State монады плюс 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)
  }
}

Смотри, мам, без перемен!

Состояние может, конечно, становиться достаточно сложным, но именно здесь линзы входят. С линзами довольно легко консультироваться и изменять (копировать с новым значением) произвольно сложные структуры данных.

Использование итераторов естественно для событий - в очень простом смысле «onTrade» становится итератором, который вызывается перечислителем (то есть «генерирует» события) с каждым событием, если оно составлено из частичной функции. сложите их все в одну частичную функцию.

В качестве альтернативы, монады состояний могут быть объединены с монадами ввода-вывода в целях понимания.

Наконец, есть возможность продолжения. Если для некоторой обработки требуется цепочка событий, то результат каждого события может быть продолжением, а сами продолжения становятся частью состояния.

2 голосов
/ 25 июля 2011

Иногда изменяемый статус необходим естественным образом. Ниже приведен пример из книги «scala by example».
Он также имеет некоторый изменяемый статус (maxBid, maxBidder) . Так что переменная не всегда плохая идея. Иногда работает нормально.

   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
               }
         }
      }
   }
}
0 голосов
/ 25 июля 2011

Я настоятельно рекомендую функциональное реактивное программирование для этой задачи. Вот разговор о такой библиотеке в scala: http://skillsmatter.com/podcast/scala/reactors

0 голосов
/ 25 июля 2011

На самом деле никогда не делал этого, но вместо изменения значений вы могли бы создавать новые экземпляры в потоке.

Затем другой процесс может выполнить итерацию этого потока, что заставит их ждать, когда они достигнут последнего экземпляра элемента потока.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...