Как написать совокупный шаблон в Scala? - PullRequest
3 голосов
/ 12 сентября 2011

Предположим, у меня есть Iterator[A] ( размер бесконечен ), и я хочу получить Iterator[B] из него, где агрегируются некоторые последующие значения типа A.

Пример: у меня естьсписок строк:

Iterator(
    "START",
    "DATA1",
    "DATA2",
    "DATA3",
    "START",
    "DATA1",
    "DATA2",
    //.. 10^10 more records
)

Я хочу объединить строки от START до NEXT START, исключая.Т.е. написать парсер.

Iterator(
"START DATA1 DATA2 DATA3",
"START DATA1 DATA2",
    //.. 10^10 / 5 more records
)

Я знаю, как это сделать обязательно, но я хочу сделать это с помощью функций scala более высокого порядка.Есть идеи?

PS EIP Aggregate http://camel.apache.org/aggregator2.html.

Ответы [ 4 ]

5 голосов
/ 13 сентября 2011

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

def aggregate(it: Iterator[String]) = new Iterator[String] {
  if (it.hasNext) it.next
  def hasNext = it.hasNext
  def next = "START " + (it.takeWhile(_ != "START")).mkString(" ")
}

, чтобы вы могли:

val i = aggregate(yourStream.iterator)
i.take(20).foreach(println) // or whatever
5 голосов
/ 13 сентября 2011

Если вы хотите функциональное решение, вы должны использовать потоки, а не итераторы (потоки неизменны). Вот один из возможных подходов:

def aggregate(strs: Stream[String] ) = { 
  aggregateRec( strs )
}

def aggregateRec( strs: Stream[String] ): Stream[String] = {
  val tail = strs.drop(1)
  if( tail.nonEmpty ) {
    val (str, rest ) = accumulate( tail )
    Stream.cons( str, aggregateRec( rest ) )
  }
  else Stream.empty
}

def accumulate( strs: Stream[String] ): (String, Stream[String])  = {
  val first = "START " + strs.takeWhile( _ != "START").mkString(" ")
  val rest = strs.dropWhile( _ != "START" )
  ( first, rest )
}

Работает как положено:

val strs = Stream( "START", "1", "2", "3", "START", "A", "B" )
val strs2 = aggregate( strs )
strs2 foreach println
1 голос
/ 12 сентября 2011

Вы можете попробовать это со сгибом:

val ls = List(
  "START",
  "DATA1",
  "DATA2",
  "DATA3",
  "START",
  "DATA1",
  "DATA2"
)

(List[List[String]]() /: ls) { (acc, elem) =>
  if (elem == "START")
    List(elem) :: acc // new head list
  else
    (elem :: acc.head) :: acc.tail // prepend to current head list
} map (_.reverse mkString " ") reverse;
0 голосов
/ 13 сентября 2011

с потоками:

object Iter {
  def main(args: Array[String]) {
    val es = List("START", "DATA1", "DATA2", "START", "DATA1", "START")
    val bit = batched(es.iterator, "START")
    println(bit.head.toList)
    println(bit.tail.head.toList)
  }

  def batched[T](it: Iterator[T], start: T) = { 
    def nextBatch(): Stream[List[T]] = { 
      (it takeWhile { _ != start }).toList match {
        case Nil => nextBatch()
        case es => Stream.cons(start :: es, nextBatch())
      }
    }
    nextBatch()
  }

}  
...