为什么Scala中的模式匹配不能使用变量?

129

请看下面的函数:

def fMatch(s: String) = {
    s match {
        case "a" => println("It was a")
        case _ => println("It was something else")
    }
}

这个模式很匹配:

scala> fMatch("a")
It was a

scala> fMatch("b")
It was something else
我想要能够做到以下事情:
def mMatch(s: String) = {
    val target: String = "a"
    s match {
        case target => println("It was" + target)
        case _ => println("It was something else")
        }
}
这会产生以下错误:
fMatch: (s: String)Unit
<console>:12: error: unreachable code
               case _ => println("It was something else")

我猜这是因为它认为target实际上是你想要指定给输入的任何内容的名称。两个问题:

  1. 为什么会出现这种行为?不能只在范围内查找具有适当类型的现有变量并首先使用它们,如果找不到,则将target视为要进行模式匹配的名称吗?

  2. 是否有解决方法?有没有办法针对变量进行模式匹配?最终可以使用大型if语句,但match case更加优雅。


相关:https://dev59.com/mWw05IYBdhLWcg3wsz3N - Dave L.
3
我相信这个问题、代码和答案在Scala 2.12.x中已经过时了。如果问题中提到适用的版本,那就更好了。请注意,我的翻译是为了让内容更加通俗易懂,但并未改变原意。 - conny
2个回答

258
你需要的是一个稳定的标识符。在Scala中,这些标识符必须以大写字母开头或用反引号括起来。
以下两种方式都可以解决你的问题:
def mMatch(s: String) = {
    val target: String = "a"
    s match {
        case `target` => println("It was" + target)
        case _ => println("It was something else")
    }
}

def mMatch2(s: String) = {
    val Target: String = "a"
    s match {
        case Target => println("It was" + Target)
        case _ => println("It was something else")
    }
}
为避免无意中引用已经存在于封闭作用域中的变量,我认为将小写模式默认视为变量而不是稳定标识符是有意义的。只有在您看到以大写字母开头或在反引号中看到时,您需要意识到它来自周围的作用域。

5
我打赌这来自 Erlang,该语言中变量以大写字母开头,符号以小写字母开头。 - Emil Ivanov
12
请注意,target是一个值(val),而不是变量(var)。它不能与变量一起使用。 - Luigi Plinge
大写字母?FORTRAN的阴影。弱鸡,马丁,弱鸡。 - Michael Lorton
15
@Emil,Scala中的大写标识符表示常量。因此,对大写标识符进行模式匹配意味着将其与常量进行比较。这实际上有助于处理像Nil这样的内容,我认为这是真正的原因。 - Daniel C. Sobral
似乎不能使用this作为一个稳定的标识符来进行模式匹配,唯一的方法似乎是使用一个等式保护器,如case x if x == this =>。这可能是一种语法上的限制,否则它至少应该在object内部语义上起作用。 - Nader Ghanbari
该规则还可以轻松地隐藏不需要的变量,例如 NonEmptyList.from(list) match { case None => Left("List was empty"); case Some(list) => /* 重新使用名称'list'表示NonEmptyList,以防止意外引用原始列表 */ } - Warbo

2

您还可以将其分配给 case 内的临时变量,然后比较它,这也可以起到作用。

def mMatch(s: String) = {
    val target: String = "a"
    s match {
        case value if (value ==target) => println("It was" + target) //else {} optional
        case _ => println("It was something else")
    }
}



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接