【问题标题】:Problem with Scala matching + scopeScala匹配+范围的问题
【发布时间】:2011-05-30 05:24:07
【问题描述】:

给定以下代码:

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 对象。

这可能吗?

【问题讨论】:

    标签: scala pattern-matching


    【解决方案1】:

    当模式匹配时,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
      }
    }
    

    【讨论】:

    • 反引号最棘手的部分是如何让它在 StackOverflow 答案中正确显示:-)
    【解决方案2】:

    这里似乎有两个混淆。第一个是由 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对象”。如果我理解正确,您正在尝试使用案例类语法构造一个对象:

    ChangeSet(field, from, to)
    

    这在模式匹配的那一侧不起作用。模式匹配的 case 端发生的事情实际上可以看作是 ChangeSet 构造的 reversematch { 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 的值但替换单个字段的新对象。案例类通过 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
      }
    }
    

    【讨论】:

      【解决方案3】:

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

      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
        }
      }
      

      【讨论】:

      • 这不太正确:您可以使用反引号表示法,如kassens's answer 所示。
      • 我不知道;至少比两年前更久:-)
      【解决方案4】:

      如果您在模式匹配中使用小写名称,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
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2015-12-18
        • 2010-11-23
        • 1970-01-01
        • 1970-01-01
        • 2022-01-23
        • 1970-01-01
        • 2011-03-10
        • 1970-01-01
        • 2018-01-04
        相关资源
        最近更新 更多