这个 case class 定义如何允许中缀模式匹配?
How does this case class definition allow infix pattern matching?
我最近使用 scala parser combinator library 编写了一个解析器。我决定对实现感到好奇,然后开始挖掘。
而reading through the code,我看到~
排序用了一个caseclass来保存左右值
附上以下评论:
/** A wrapper over sequence of matches.
*
* Given `p1: Parser[A]` and `p2: Parser[B]`, a parser composed with
* `p1 ~ p2` will have type `Parser[~[A, B]]`. The successful result
* of the parser can be extracted from this case class.
*
* It also enables pattern matching, so something like this is possible:
*
* {{{
* def concat(p1: Parser[String], p2: Parser[String]): Parser[String] =
* p1 ~ p2 ^^ { case a ~ b => a + b }
* }}}
*/
case class ~[+a, +b](_1: a, _2: b) {
override def toString = "("+ _1 +"~"+ _2 +")"
}
既然提到的这样的代码肯定是可能的,并且使用a ~ b
定义的解析器可以通过{ case a ~ b => ... }
提取到值中,那么这个反应用程序究竟是如何工作的呢?我知道 scala 中的 unapply
方法,但此处提供了 none 。 case classes 是否默认提供一个(我想是的)?如果是这样,这个特殊情况 class 是如何变成 case a ~ b
而不是 case ~(a,b)
的?这是 Scala 程序员可以利用的模式吗?
这与此问题中的 不同,因为不存在 unapply 方法——或者存在吗? case classes 会自动神奇地接收未应用的方法吗?
Do case classes provide [unapply
] by default (I think yes)?
你的怀疑是正确的。 unapply()
是案例 class 中自动提供的众多内容之一。您可以通过执行以下操作自行验证:
- 在自己的文件中写一个简单的
class
定义。
- 仅通过"typer"阶段编译文件并保存结果。 (调用
scalac -Xshow-phases
查看所有编译器阶段的描述。)
- 编辑文件。在 class 定义前添加单词
case
。
- 重复步骤 2。
- 比较两个保存的结果。
来自 Bash shell 它可能看起来像这样。
%%> cat srcfile.scala
class XYZ(arg :Int)
%%> scalac -Xprint:4 srcfile.scala > plainClass.phase4
%%> vi srcfile.scala # add “case”
%%> scalac -Xprint:4 srcfile.scala > caseClass.phase4
%%> diff plainClass.phase4 caseClass.phase4
编译器会产生大量噪音,但您会发现,只需将 case
添加到 class,编译器就会生成大量额外代码。
一些注意事项:
case class
实例
- 有
Product
和 Serializable
混合到类型
- 提供public对构造函数参数的访问
- 有方法
copy()
、productArity
、productElement()
和 canEqual()
。
- 覆盖(提供新代码)方法
productPrefix
、productIterator
、hashCode()
、toString()
和 equals()
伴随对象(由编译器创建)
- 有方法
apply()
和 unapply()
- 覆盖
toString()
If so, how does this particular case class become case a ~ b
and not case ~(a,b)
?
事实证明这是该语言提供的一个很好的(如果相当晦涩)便利。
unapply()
调用returns一个Tuple
可以被模式化为中缀符号。同样,这很容易验证。
class XX(val c:Char, val n:Int)
object XX {
def unapply(arg: XX): Option[(Char, Int)] = Some((arg.c,arg.n))
}
val a XX b = new XX('g', 9)
//a: Char = g
//b: Int = 9
我最近使用 scala parser combinator library 编写了一个解析器。我决定对实现感到好奇,然后开始挖掘。
而reading through the code,我看到~
排序用了一个caseclass来保存左右值
附上以下评论:
/** A wrapper over sequence of matches.
*
* Given `p1: Parser[A]` and `p2: Parser[B]`, a parser composed with
* `p1 ~ p2` will have type `Parser[~[A, B]]`. The successful result
* of the parser can be extracted from this case class.
*
* It also enables pattern matching, so something like this is possible:
*
* {{{
* def concat(p1: Parser[String], p2: Parser[String]): Parser[String] =
* p1 ~ p2 ^^ { case a ~ b => a + b }
* }}}
*/
case class ~[+a, +b](_1: a, _2: b) {
override def toString = "("+ _1 +"~"+ _2 +")"
}
既然提到的这样的代码肯定是可能的,并且使用a ~ b
定义的解析器可以通过{ case a ~ b => ... }
提取到值中,那么这个反应用程序究竟是如何工作的呢?我知道 scala 中的 unapply
方法,但此处提供了 none 。 case classes 是否默认提供一个(我想是的)?如果是这样,这个特殊情况 class 是如何变成 case a ~ b
而不是 case ~(a,b)
的?这是 Scala 程序员可以利用的模式吗?
这与此问题中的
Do case classes provide [
unapply
] by default (I think yes)?
你的怀疑是正确的。 unapply()
是案例 class 中自动提供的众多内容之一。您可以通过执行以下操作自行验证:
- 在自己的文件中写一个简单的
class
定义。 - 仅通过"typer"阶段编译文件并保存结果。 (调用
scalac -Xshow-phases
查看所有编译器阶段的描述。) - 编辑文件。在 class 定义前添加单词
case
。 - 重复步骤 2。
- 比较两个保存的结果。
来自 Bash shell 它可能看起来像这样。
%%> cat srcfile.scala
class XYZ(arg :Int)
%%> scalac -Xprint:4 srcfile.scala > plainClass.phase4
%%> vi srcfile.scala # add “case”
%%> scalac -Xprint:4 srcfile.scala > caseClass.phase4
%%> diff plainClass.phase4 caseClass.phase4
编译器会产生大量噪音,但您会发现,只需将 case
添加到 class,编译器就会生成大量额外代码。
一些注意事项:
case class
实例
- 有
Product
和Serializable
混合到类型 - 提供public对构造函数参数的访问
- 有方法
copy()
、productArity
、productElement()
和canEqual()
。 - 覆盖(提供新代码)方法
productPrefix
、productIterator
、hashCode()
、toString()
和equals()
伴随对象(由编译器创建)
- 有方法
apply()
和unapply()
- 覆盖
toString()
If so, how does this particular case class become
case a ~ b
and notcase ~(a,b)
?
事实证明这是该语言提供的一个很好的(如果相当晦涩)便利。
unapply()
调用returns一个Tuple
可以被模式化为中缀符号。同样,这很容易验证。
class XX(val c:Char, val n:Int)
object XX {
def unapply(arg: XX): Option[(Char, Int)] = Some((arg.c,arg.n))
}
val a XX b = new XX('g', 9)
//a: Char = g
//b: Int = 9