模式匹配Scala中列表的结尾/中间

Hug*_*ira 6 scala pattern-matching

有人可以给我一个更简单的解决方案来解决以下代码(在给定结构的情况下展开整数列表0xFC :: len :: payload :: ... :: 0x0A :: 0x0D):

object Payload {
  def unapply(z: List[Int]): Option[List[Int]] = if (z.length == z.head + 1) Some(z tail) else None
}

object EndToken {
  def unapply(z: List[Int]): Option[List[Int]] = z.reverse match {
    case 0x0D :: 0x0A :: tail => Some(tail.reverse)
    case _ => None
  }
}

object Message {
  def unapply(z: List[Int]): Option[List[Int]] = z match {
    case 0xFC :: EndToken(x) => Some(x)
    case _ => None
  }
}

object Main extends App {
  val x = List(0xFC, 0x03, 0x01, 0x02, 0x03, 0x0A, 0x0D)

  x match {
    case Message(Payload(payload)) => println (payload)
    case _ => println("No match")
  }
}
Run Code Online (Sandbox Code Playgroud)

就像是:

object Message {
  def unapply(z: List[Int]): Option[List[Int]] = z match {
    case 0xFC :: Payload(x) :: 0x0A :: 0x0D => Some(x)
    case _ => None
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,当然,::期待元素,而不是列表,所以它不起作用......

huy*_*hjl 3

这是我的解决方案(尽管重新阅读后我认为这就像丹尼尔的解决方案)。它基于中缀操作模式,其中模式op(p, q)相同p op q

运算符以 开头,:具有相同的优先级,以::结尾,与:右侧关联。(len, payload) :!: tail是相同的:!:((len, payload), tail)。基于长度的有效负载提取的实现有点复杂,但主要是因为我只想遍历列表一次。

object :!: {
  type LengthPayload = (Int, List[Int]) // (len, payload)
  // returns ((len, payload), unparsed)
  def unapply(z: List[Int]): Option[(LengthPayload, List[Int])] = {
    if (z == Nil) None 
    else {
      val len = z.head
      // use ListBuffer to traverse the list only once
      val buf = collection.mutable.ListBuffer[Int]()
      def take(l: Int, list: List[Int]): Option[(LengthPayload, List[Int])] = {
        list match {
          case Nil if l > 0 => None
          case _ if l == 0 => Some((len, buf.toList), list)
          case _ => buf += list.head; take(l - 1, list.tail)
        }
      }
      take(len, z.tail)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后消息变得更简单(视觉上):

object Message {
  def unapply(z: List[Int]): Option[List[Int]] = z match {
    case 0xFC :: (len, payload) :!: 0x0A :: 0x0D :: Nil => Some(payload)
    case _ => None
  }
}
Run Code Online (Sandbox Code Playgroud)

结果:

val x = List(0xFC, 0x03, 0x01, 0x02, 0x03, 0x0A, 0x0D)
x match {
  case Message(payload) => println(payload)
  case _ => println("No match")
}
// List(1, 2, 3)
Run Code Online (Sandbox Code Playgroud)