简体   繁体   English

与Scala中的任意列表大小匹配

[英]Match with arbitrary list size in Scala

Suppose I have an infinite list of stuff. 假设我有一个无限的东西列表。 In this list, I sometimes have something indicating that an hidden message is about to begin, then a message length, a crc, and then an end token. 在这个列表中,我有时会有一些东西表明隐藏的消息即将开始,然后是消息长度,crc,然后是结束令牌。 Then the list continues, and somewhere, a new message appears: 然后列表继续,在某处,会出现一条新消息:

a :: b :: start :: 3 :: 1 :: 2 :: 3 :: 4FAA :: end :: x :: y :: z :: .... 

What is the most idiomatic (using match , I think?) of pattern matching this to a structure like: 将这种模式匹配到以下结构的最惯用(使用match ,我认为?)是什么?

size = 3
payload = 1 :: 2 :: 3
crc = 4FAA

Also, take into consideration that the token "start" may appear inside the payload, so one must rely on a "full match". 另外,考虑到令牌“start”可能出现在有效载荷内部,因此必须依赖“完全匹配”。

Use parsers combinator. 使用解析器组合器。 The exact solution for you seems to be parsing tokens, but, to simplify, I'll assume you're just reading a string of words separated by spaces. 你的确切解决方案似乎是解析令牌,但为了简化,我假设你只是在阅读一系列用空格分隔的单词。

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
}

And, using it: 并且,使用它:

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)

Now, RegexParsers parse a stream of Char . 现在, RegexParsers解析了Char的流。 Since you have a stream of tokens, StandardTokenParsers might be a more suited class. 由于您有一个令牌流,因此StandardTokenParsers可能是更适合的类。 Or you may base it on Parsers , and define Elem to whatever fit your needs. 或者您可以将其基于Parsers ,并将Elem定义为适合您的需求。

The grammar you've described is a regular language . 你所描述的语法是常用语言 Probably best to define a custom extractor object with an unapply method that can parse your list of tokens into a list of your structures. 可能最好使用unapply方法定义自定义提取器对象 ,该方法可以将令牌列表解析为结构列表。

I think there are many different ways to solve this. 我认为有很多不同的方法可以解决这个问题。 Top of mind for me would be the following recursive solution: 对我来说最重要的是以下递归解决方案:

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
    }
 }

This approach would return: 这种方法将返回:

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))

If you have a 'really long' (infinite) list you should make this algorithm tail recursive. 如果你有一个'真的很长'(无限)列表,你应该使这个算法尾递归。 The tail-recursive solution would look like this (yields the same result as above): 尾递归解决方案看起来像这样(产生与上面相同的结果):

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))

In terms of processing I would pass in a function that processes a message instead of concatenating each message in a list. 在处理方面,我会传递一个处理消息的函数,而不是连接列表中的每个消息。 This solution would look like this: 此解决方案如下所示:

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
    }
}

Usage: 用法:

scala> processMessages(l) { println }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM