如何基于一个键组合两个枚举器(在Dones之间维护Iteratee状态)?

时间:2014-03-14 16:23:28

标签: scala playframework-2.2 iterate

我正在尝试将两个Play框架枚举器组合在一起,但合并具有相同键值的值。在大多数情况下它都有效,除了用于保存以前没有匹配的先前值的地图在每次找到匹配并且返回完成的迭代时丢失。

有没有办法在返回完成后提供状态到下一次调用步骤?

到目前为止,我发现的任何示例似乎都是将连续值组合在一起然后传递整个分组,并且没有从流中对某些任意值进行分组,并且只有在分组后才传递特定值。

理想情况下,一旦匹配完成,它将发送匹配的值。 到目前为止我所得到的(几乎基于Creating a time-based chunking Enumeratee

def virtualSystemGrouping[E](system:ParentSystem): Iteratee[Detail, Detail] = {
  def step(state: Map[String, Detail])(input:Input[Detail]): Iteratee[Detail, Detail] = {
    input match {
      case Input.EOF => {Done(null, Input.EOF)}
      case Input.Empty =>{Cont[Detail, Detail](i => step(state)(i))}
      case Input.El(e) => {
        if (!system.isVirtual) Done(e)
        if (state.exists((k) =>{k._1.equals(e.name)})) {
          val other = state(e.name)
          // ??? should have a; state - e.name
          // And pass new state and merged value out.
          Done(e + other)
        } else {
          Cont[Detail, Detail](i => step(state + (e.name -> e))(i))
        }
      }
    }
  }
  Cont(step(Map[String,Detail]()))
}

调用这个看起来像;

   val systems:List[ParentSystem] = getSystems()
   val start = Enumerator.empty[Detail]
   val send = systems.foldLeft(start){(b,p) =>
     b interleave Concurrent.unicast[Detail]{channel =>
       implicit val timeout = Timeout (1 seconds)
       val actor = SystemsActor.lookupActor(p.name + "/details")
       actor map {
         case Some(a) => {a ! SendDetailInformation(channel)}
         case None => {channel.eofAndEnd}
         } recover {
         case t:Throwable => {channel.eofAndEnd}
       }

       }
   } &> Enumeratee.grouped(virtualSystemGrouping(parent)) |>> Iteratee.foreach(e => {output.push(e)})
   send.onComplete(t => output.eofAndEnd)

1 个答案:

答案 0 :(得分:0)

我能够提出的一种方法是使用Concurrent.unicast并将通道传递给组合功能。我确信有一种方法可以创建一个Iteratee / Enumerator,它可以在一个漂亮的整齐包中完成所有工作,但这暂时让我不知所措。

更新了组合功能;

def virtualSystemGrouping[E](system:ParentSystem, output:Channel): Iteratee[Detail, Detail] = {
  def step(state: Map[String, Detail])(input:Input[Detail]): Iteratee[Detail, Detail] = {
    input match {
      case Input.EOF => {
          state.mapValues(r=>output.push(r))
          output.eofAndEnd
          Done(null, Input.EOF)
      }
      case Input.Empty =>{Cont[Detail, Detail](i => step(state)(i))}
      case Input.El(e) => {
        if (!system.isVirtual) {output.push(e); Done(e, Input.Empty)}
        if (state.exists((k) =>{k._1.equals(e.name)})) {
          val other = state(e.name)
          output.push(e + other)
          Cont[Detail, Detail](i => step(state - e.name)(i))
        } else {
          Cont[Detail, Detail](i => step(state + (e.name -> e))(i))
        }
      }
    }
  }
  Cont(step(Map[String,Detail]()))
}

此处将任何组合值推入输出通道,然后进行处理。

使用方法如下所示;

   val systems:List[ParentSystem] = getSystems(parent)
   val start = Enumerator.empty[Detail]
   val concatDetail = systems.foldLeft(start){(b,p) =>
     b interleave Concurrent.unicast[Detail]{channel =>
       implicit val timeout = Timeout (1 seconds)
       val actor = SystemsActor.lookupActor(p.name + "/details")
       actor map {
         case Some(a) => {a ! SendRateInformation(channel)}
         case None => {channel.eofAndEnd}
         } recover {
         case t:Throwable => {channel.eofAndEnd}
       }

       }
   } 


   val combinedDetail = Concurrent.unicast[Detail]{channel => 
    concatDetail &> Enumeratee.grouped(virtualSystemGrouping(parent, channel)) |>> Iteratee.ignore
   }
   val send = combinedDetail |>> Iteratee.foreach(e => {output.push(e)})
   send.onComplete(t => output.eofAndEnd)

与原版非常相似,但现在调用组合函数是在单播onStart块(定义了通道)内完成的。 concatDetail是根据子系统的交错结果创建的枚举器。这通过系统分组功能提供,该功能又通过提供的通道推送任何组合结果(以及EOF的剩余结果)。

然后将combinedDetails枚举器输入并推送到上游输出通道。

编辑: virtualSystemGrouping可以概括为;

  def enumGroup[E >: Null, K, M](
      key:(E) => K,
      merge:(E, Option[E]) => M,
      output:Concurrent.Channel[M]
      ): Iteratee[E, E] = {
    def step(state: Map[K, E])(input:Input[E]): Iteratee[E, E] = {
      input match {
        case Input.EOF => {
          state.mapValues(f => output.push(merge(f, None))) //Push along any remaining values.
          output.eofAndEnd();
          Done(null, Input.EOF)
          }
        case Input.Empty =>{ Cont[E, E](i => step(state)(i))}
        case Input.El(e) => {
          if (state.contains(key(e))) {
            output.push(merge(e, state.get(key(e))))
            Cont[E, E](i => step(state - key(e))(i))
          } else {
            Cont[E, E](i => step(state + (key(e) -> e))(i))
          }
        }
      }
    }

    Cont(step(Map[K,E]()))
  }

通过电话,如;

Enumeratee.grouped(
             enumGroup(
             (k=>k.name),
             ((e1, e2) => e2.fold(e1)(v => e1 + v)),
             channel)
            )