Scala 中列表的模式匹配末尾/中间
简单的解决方案来解决以下代码(它正在展开给定结构 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")
}
}
有人可以给我一个更 就像:
object Message {
def unapply(z: List[Int]): Option[List[Int]] = z match {
case 0xFC :: Payload(x) :: 0x0A :: 0x0D => Some(x)
case _ => None
}
}
但是,当然, ::
需要的是元素,而不是列表,所以它不起作用......
Can someone give me a simpler solution to the following code (which is unfolding a list of integers given a structure 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")
}
}
Something like:
object Message {
def unapply(z: List[Int]): Option[List[Int]] = z match {
case 0xFC :: Payload(x) :: 0x0A :: 0x0D => Some(x)
case _ => None
}
}
But, of course, ::
is expecting elements, not lists, so it doesn't work...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这是我的解决方案(尽管重新阅读后我认为这就像丹尼尔的解决方案)。它基于中缀操作模式,其中模式
op(p, q)
与p op q
相同。该运算符以
:
开头,与::
具有相同的优先级,以:
结尾,与右侧关联。(len, Payload) :!: tail
与:!:((len, Payload), tail)
相同。基于长度的有效负载提取的实现有点复杂,但主要是因为我只想遍历列表一次。然后消息变得更简单(视觉上):
结果:
Here is my solution (though on re-reading I think it's like Daniel's solution). It is based on a infix operation pattern where the pattern
op(p, q)
is the samep op q
.The operator starts with
:
to have same precedece as::
and ends with:
to associate to the right.(len, payload) :!: tail
is the same as:!:((len, payload), tail)
. The implementation of the payload extraction based on length is a bit more complex but mostly because I wanted to traverse the list only once.Then message becomes simpler (visually):
The result:
Scala 现在使用“:+”库对象支持序列末尾的模式匹配。我不确定这个功能是什么时候添加的,但我在 Dean Wampler 和 Alex Payne 的《Programming Scala》第二版中读到了它。下面是一个检索列表中最后一个元素的字符串的简单示例:
Pattern matching on the end of a sequence is now supported in Scala using the ':+' library object. I'm not sure when this functionality was added, but I read about it in the 2nd edition of Programming Scala by Dean Wampler and Alex Payne. Here is a simple example of retrieving a string of the last element in a list:
您可以在此处利用一些语法糖进行模式匹配:
与以下相同:
因此,如果您像这样修改 EndToke 提取器:
您可以在如下模式中使用它:(
抱歉,我不记得优先级规则了手,所以其中一些括号可能是不必要的。)
You can take advantage of a bit of syntactic sugar for pattern matching here:
is the same as:
So if you modify your EndToke extractor like so:
You can use it in patterns like:
(Sorry, I don't remember the precedence rules off hand so some of those parens may be unnecessary.)
您不能将参数传递给匹配项,除非隐式传递,并且提取器必须知道它需要提取什么。
人们无法真正简化您的解决方案。这是另一种编写方式,但这更多的是偏好问题。
编辑:注意方法
:<<:
和:>>:
上的冒号。在此代码中,后者实际上并不需要它们,但前者需要它们。标识符末尾的冒号表示左关联性和右关联性。这很重要,因为
::
和:<<:
右侧的参数必须是List
,但0x0A
和0x0D
不是列表。然而,右结合性意味着首先应用最右边的运算符,然后应用左边的运算符。换句话说。0x0A :: (0x0D :: Nil)
而不是(0x0A :: 0x0D) :: Nil
。由于优先级,标识符开头的冒号是必需的。即使具有右关联性,错误的优先级也会将
0xFC :: Payload <<: ...
变为(0xFC :: Payload) <<: ...
代码>.请注意,我使用
unapplySeq
在Message
中返回结果,以便可以像在List
中一样提取结果。然而,这意味着您需要@ _*
来获取整个序列:You can't pass parameters to a match, except implicitly, and the extractor must know what it needs to extract.
One can't really simplify your solution much. Here's an alternative way of writing it, but it's more a matter of preference than anything else.
Edit: Note about the colons on methods
:<<:
and:>>:
. They aren't really needed for the latter in this code, but they are needed for the former.The colon at the end of the identifier is about left- and right-associativity. This is important because the argument to the right of
::
and:<<:
must be aList
, but0x0A
and0x0D
are not lists. Right associativity, however, means the right-most operator is applied first, and the ones to the left apply over the result. In other words.0x0A :: (0x0D :: Nil)
instead of(0x0A :: 0x0D) :: Nil
.The colon at the beginning of the identifier is required because of precedence. Even with the right-associativity, wrong precedence would turn
0xFC :: payload <<: ...
into(0xFC :: payload) <<: ...
.Note that I'm using
unapplySeq
to return the results inMessage
, so that can be extracted like inList
. That means, however, that you need@ _*
to get the whole sequence: