过滤关于某些州的收集

时间:2018-01-15 18:00:43

标签: scala

抱歉模糊描述,但我无法描述得更好。那么,问题 - 我有类的层次结构

sealed trait GameEvent
case object RoundStarted extends GameEvent
case object MessageSent extends GameEvent
....

解析游戏数据后,我有List [GameEvent]。根据业务逻辑,我需要提供在特定轮次“观看”游戏的能力。由RoundStartedEvent确定的每轮开始。 API方法有以下签名:

def load(id:Int, round:Int) = {
   val game = repo.load(id)
   val view = game.dropToRound(round)
   view
}
case class Game(id:Int, events:List[GameEvent]){
   def dropToRound(round:Int) = {
       val newEvents = //events.filter() How? I need find index of "round"-th RoundStarted event and get all elements before it
       this.copy(events = newEvents)
   }
}

val testData = Game(1, List(RoundStarted//round 0, MessageSent, MessageSent, RoundStarted//round 1, MessageSent, RoundStarted//round 2))
//To retrieve all events before round 2 we calling load(1, 1)
assert(load(1, 1) shouldBe (Game(1,List(RoundStarted//round 0, MessageSent, MessageSent)))

我知道如何做到这一点,但是从功能上来说,更好的方法是什么?最好没有像scalaz这样的库,但如果真的很简洁 - 我也会接受它:)

2 个答案:

答案 0 :(得分:2)

您实际上无法使用过滤器,因为您只想在特定情况发生之前收集事件。

此功能将收集事件:

  def collectEvents(round: Int, events: List[GameEvent]): List[GameEvent] = {
    def collectEventsList(r: Int, eventList: List[GameEvent], collectedEvents: List[GameEvent]): List[GameEvent] = {
      eventList match {
        case RoundStarted :: _ if r == 0 =>
          collectedEvents
        case RoundStarted :: y if r > 0 =>
          collectEventsList(r - 1, y, RoundStarted :: collectedEvents)
        case x :: y =>
          collectEventsList(r, y, x :: collectedEvents)
        case List() =>
          collectedEvents
      }
    }
    collectEventsList(round, events, List()).reverse
  }

答案 1 :(得分:0)

这可能是两个世界中最糟糕的:它使用takeWhile和可变var。它几乎等同于命令式解决方案。但至少它很短:

case class Game(id: Int, events: List[GameEvent]) {
  def dropToRound(round: Int): Game = {
    var cnt = 0
    val newEvents = events.takeWhile({
      case RoundStarted => cnt += 1
        cnt <= round
      case _ => true
    })
    this.copy(events = newEvents)
  }
}

<强>更新

以下是命令性代码的更纯粹的翻译:

case class Game(id: Int, events: List[GameEvent]) {

  def dropToRound(round: Int): Game = {
    val newEvents = events.foldLeft((List.empty[GameEvent], 0))((t, ev) => (t, ev) match {
      case ((out, cnt), _) if cnt > round => t
      case ((out, cnt), RoundStarted) if cnt == round => t
      case ((out, cnt), RoundStarted) => (ev :: out, cnt + 1)
      case ((out, cnt), _) => (ev :: out, cnt)
    })._1.reverse

    this.copy(events = newEvents)
  }
}

明显的缺点是:

  1. 你无法阻止foldLeft所以它无论如何都要经过整个事件列表
  2. 最后添加reverse