假设我有无限的东西列表。在这个列表中,我有时会有一些东西表明隐藏的消息即将开始,然后是消息长度,crc,然后是结束令牌。然后列表继续,在某处,会出现一条新消息:
a :: b :: start :: 3 :: 1 :: 2 :: 3 :: 4FAA :: end :: x :: y :: z :: ....
最常用的(使用match
,我认为?)模式匹配的结构如下:
size = 3
payload = 1 :: 2 :: 3
crc = 4FAA
另外,考虑到令牌“start”可能出现在有效载荷内部,因此必须依赖“完全匹配”。
答案 0 :(得分:8)
使用解析器组合子。对你来说,确切的解决方案似乎是解析令牌,但为了简化,我假设你只是在读一串由空格分隔的单词。
object P extends scala.util.parsing.combinator.RegexParsers {
def message: Parser[Any] = properMessage | dummy ~> message
def properMessage = start ~> body <~ end
def start = "(?i)start".r
def end = "(?i)end".r
def body = (size >> payload) ~ crc
def crc = word
def size = "\\d+".r ^^ (_.toInt)
def payload = repN(_: Int, word)
def word = "\\S+".r
def dummy = word
}
然后,使用它:
scala> val stream = "a b start 3 1 2 3 4FAA end x y z "
stream: String = "a b start 3 1 2 3 4FAA end x y z "
scala> P.parse(P.message, stream)
res5: P.ParseResult[Any] = [1.35] parsed: (List(1, 2, 3)~4FAA)
现在,RegexParsers
解析了Char
的流。由于您有一个令牌流,StandardTokenParsers
可能是更适合的类。或者您可以将其基于Parsers
,并根据您的需要定义Elem
。
答案 1 :(得分:2)
您所描述的语法是常规语言。可能最好使用unapply
方法定义自定义extractor object,该方法可以将您的令牌列表解析为结构列表。
答案 2 :(得分:2)
我认为有很多不同的方法可以解决这个问题。对我来说最重要的是以下递归解决方案:
def filterMessages(l:List[Any]):List[List[Any]] = {
l match {
case "start" :: tail => tail.takeWhile(_ != "end") :: filterMessages(tail.dropWhile(_ != "end"))
case a :: tail => filterMessages(tail)
case Nil => Nil
}
}
这种方法会回归:
scala> val l = "a" :: "b" :: "start" :: 2 :: 1 :: 2:: "crc" :: "end" :: "a" :: "x" :: "start" :: 3 :: 1 :: 2 ::3 :: "crc" :: "end" :: "x" :: Nil
scala> println(filterMessages(l))
res0: List(List(2, 1, 2, crc), List(3, 1, 2, 3, crc))
如果你有一个'真的很长'(无限)列表,你应该使这个算法尾递归。尾递归解决方案看起来像这样(产生与上面相同的结果):
import scala.annotation.tailrec
@tailrec def filterMessages(l:List[Any], result:List[List[Any]]):List[List[Any]] = {
l match {
case "start" :: tail => filterMessages(tail.dropWhile(_ != "end"), tail.takeWhile(_ != "end") :: result)
case a :: tail => filterMessages(tail, result)
case Nil => result
}
}
scala> println(filterMessages(l, Nil))
res0: List(List(2, 1, 2, crc), List(3, 1, 2, 3, crc))
在处理方面,我会传递一个处理消息的函数,而不是连接列表中的每个消息。这个解决方案看起来像这样:
def processMessages(l: List[Any])(process: (List[Any]) => Unit): Unit = {
l match {
case "start" :: tail => process(tail.takeWhile(_ != "end")); processMessages(tail.dropWhile(_ != "end"))(process)
case a :: tail => processMessages(tail)(process)
case Nil => Nil
}
}
用法:
scala> processMessages(l) { println }