是否可以在 Scala 匹配器中使用算术?

Is it possible to use arithmetic in scala matchers?

考虑代码:

def MatchSmth(someInt: Int, offset: Int = 1): Int  = {
  someInt match {
    case `offset` + 3 => 123123
    case `offset` + 4 => 22
    case `offset` + 5 => 123
    case invalid => -1
  }
 }

编译错误:scala not found: value +

等效1:

def MatchSmth(someInt: Int, offset: Int = 1): Int  = {
  if (someInt == offset + 3) 123123
  else if (someInt == offset + 4) 22
  else if (someInt == offset + 5) 123
  else -1
 }

等效2:

def MatchSmth(someInt: Int, offset: Int = 1): Int  = {
  someInt match {
    case v if v == `offset` + 3 => 123123
    case v if v == `offset` + 4 => 22
    case v if v == `offset` + 5 => 123
    case invalid => -1
  }
 }

除equivalent1和equivalent2外,是否有与此代码类似的内容?为什么 Scala 不允许在匹配器中进行这种构造(使用算术)?

你可以写

def MatchSmth(someInt: Int, offset: Int = 1): Int  = {
   (someInt - offset) match {
       case 3 => 123123
       case 4 => 22
       case 5 => 123
       case _ => -1
   }
}

没有。模式的 Scala grammar 是:

8.1 Patterns

Syntax:
Pattern       ::= Pattern1 { ‘|’ Pattern1 }
Pattern1      ::= varid ‘:’ TypePat
              | ‘_’ ‘:’ TypePat
              | Pattern2
Pattern2      ::= varid [‘@’ Pattern3]
              | Pattern3
Pattern3      ::= SimplePattern
              | SimplePattern {id [nl] SimplePattern}
SimplePattern ::= ‘_’
              | varid
              | Literal
              | StableId
              | StableId ‘(’ [Patterns] ‘)’
              | StableId ‘(’ [Patterns ‘,’] [varid ‘@’] ‘_’ ‘*’ ‘)’
              | ‘(’ [Patterns] ‘)’
              | XmlPattern
Patterns      ::= Pattern {‘,’ Patterns}

如您所见,literal(8.1.4 文字模式)或 StableId(8.1.5 稳定标识符模式)是允许的,而不是常量表达式。您现在可以问 - 是否有一些重要原因不允许常量表达式?如果允许常量表达式,语法是否仍然可以明确地工作?那个我不知道。

技术原因

从技术上讲,Scala 在其规范中不允许这样做。这已在 .

中描述

概念原因

从概念上讲,模式匹配的想法不是成为 if-then-else 的捷径或求解方程,而是提供使用解构定义的偏函数,类似于 Lisp。对于这个任务,Scala 使用所谓的 "extractors".

抽取器与构造器有点相反。从技术上讲,Scala 使用方法 "unapply" 将给定的对象分解为多个部分。 Unapply 接受对象和 returns 一个布尔值、一个可选的原子值或一个可选的元组。或者还有 unapplySeq,它可以 return 一个值序列。 Scala 然后尝试将结果与给定的参数列表相匹配。当可能匹配时,Scala 解开原子值或元组中具有给定参数名称的部分。 (有关详细信息,请参阅 this paper

示例:

case class Pet(name: String, age : Int)
Pet("Polly", 86) match {
   case Pet(name, _) => println(s"Hello ${name}")
}
// This will print: Hello Polly

Scala 将创建一个对象 Pet("Polly", 86)。然后它将那个对象赋予 match 之后定义的偏函数。此函数将对该对象调用 Pet.unapply(...) 并检查结果是否具有 Some(Tuple[Int,_]) 的形状。如果为真,它将变量 name 绑定到该元组的第一个成员,并使用 println 函数调用给定的操作。

Scala 只检查 unapply 结果的形状。从理论上讲,它可以付出更多的努力来尝试将 unapply 的结果与给定的变量统一起来。这将有助于简单的情况,例如您的示例中的情况。但在更复杂的情况下,它会带来巨大的运行时间损失。理论上,统一甚至可以无限循环。

摘要 (tl;dr)

Match不是花哨的if,而是使用了解构。它并没有在统一上付出任何努力,而是采用了术语 "as-is"。这是保持生成代码快速的必要条件。