Scala匹配+范围的问题

时间:2011-05-30 05:24:08

标签: scala pattern-matching

给出以下代码:

case class ChangeSet(field:String, from:Object, to:Object)

private var changed:List[ChangeSet] = Nil

def change(field:String, from:Object, to:Object) {
  changed.find{ case ChangeSet(field,_,_) => true } match {
    case Some(ChangeSet(field,to,_)) => // do stuff
    case Some(_) => // do stuff
    case _ => // do stuff
  }
}

给我带来麻烦的一行是Some(ChangeSet(field,to,_))

它编译但似乎正在发生的事情是Scala将其填充为通配符的占位符。我的假设基于以下事实:当我执行以下Some(ChangeSet(field,to,to))时,我收到错误to is already defined as value

我想要的是从方法参数中使用to创建一个ChangeSet对象。

这可能吗?

4 个答案:

答案 0 :(得分:7)

当模式匹配时,Scala会将所有以小写字母开头的标识符解释为占位符并填充值。要告诉Scala使用to作为外部作用域的常量值,您需要使用反引号围绕它:`to`。或者,您可以像Rex Kerr建议的那样将to的名称更改为To,但我更喜欢将变量保持为小写。

这应该有效:

def change(field:String, from:Object, to:Object) {
  changed.find{ case ChangeSet(`field`,_,_) => true } match {
    case Some(ChangeSet(`field`, `to`, _)) => // do stuff
    case Some(_) => // do stuff
    case _ => // do stuff
  }
}

答案 1 :(得分:4)

这里似乎有两个混乱。第一个是Rex和Kim确定的那个。您可以从 Scala中的编程中阅读this section以获取更多信息。归结为:

x match { case Some(foo) => } // variable pattern, defines and binds variable foo
x match { case Some(Foo) => } // constant pattern, based on val Foo
x match { case Some(`foo`) => } // constant pattern for lowercase val

您还可以使用警卫来约束匹配

x match { case Some(foo) if condition => }

第二个困惑是你要“使用方法参数”创建一个ChangeSet对象。如果我理解正确,你试图使用case类语法构造一个对象:

ChangeSet(field, from, to)

这在模式匹配的那一方面不起作用。在模式匹配的 case 端发生的事情实际上可以看作是ChangeSet构造的 reverse match { case ChangeSet(field, from, to) => }有点解构您的ChangeSet对象,并将其部分分配给fieldfromto个值。当它像这样组成时也是如此:Some(ChangeSet(field, from, to)),它首先解构Some然后ChangeSet。您可以看到正在使用值定义,因为它正在利用相同的解构机制:

scala> val cset = ChangeSet("a", "from", "to")
cset: ChangeSet = ChangeSet(a,from,to)

scala> val Some(ChangeSet(s, o1, o2)) = Some(cset)
s: String = a
o1: java.lang.Object = from
o2: java.lang.Object = to

您希望做的是构建一个复制ChangeSet的值但替换单个字段的新对象。 Case类支持copy,继续我的REPL示例:

scala> val cset2 = cset.copy(from = o2)
cset2: ChangeSet = ChangeSet(a,to,to)

考虑到这一点,这是change的另一个建议:

def change(field:String, from:Object, to:Object) {
  changed.find(_.field == field) match {
    case Some(cset) =>
      val csetnew = cset.copy(from = to)
      // do stuff with csetnew
    case None =>
      // do stuff
  }
}

答案 2 :(得分:1)

您不能在这样的模式中使用变量名称。您必须先将其与新变量匹配,然后进行显式比较。

def change(field:String, from:Object, to:Object) {
  changed.find{ case ChangeSet(f,_,_) => field == f } match {
    case Some(ChangeSet(f,too,_)) if f == field && to == too => // do stuff
    case Some(_) => // do stuff
    case _ => // do stuff
  }
}

答案 3 :(得分:1)

如果在模式匹配中使用小写名称,Scala将填写该值。如果您只想匹配该值,则需要使用大写名称。撇开你要做的事情的逻辑,以及名字排序的可疑变化,你想要:

def change(Field: String, from:Object, To: Object) {
  changed.find{
    case ChangeSet(Field,_,_) => true
    case _ => false   // You need this line!  No match is an exception, not false!
  } match {
    case Some(ChangeSet(Field,To,_)) => // do stuff
    case Some(_) => // do stuff
    case _ => // do stuff
  }
}