【问题标题】:Use same variable multiple times within one pattern在一个模式中多次使用相同的变量
【发布时间】:2012-12-20 00:30:56
【问题描述】:

我希望能够在一个模式中多次使用单个变量,这样只有在所有位置都存在相同值时它才会匹配,例如

list match {
  case x :: x :: xs => // recurse
}

匹配List(1,1,2),但不匹配List(1,2,1)。但这不能与 error: x is already defined as value x 编译。

在研究这个问题时,我发现我也可以在 case 子句中包含一个守卫,所以我可以这样做

list match {
  case x1 :: x2 :: xs if x1==x2 => // recurse
}

这似乎以相同的方式工作(确实如此,对吗?)。这很好,但如果我想在很多地方都使用相同的值,它看起来就不会那么干净了,比如

list match {
  case x1::x2::x3::x4::xs if x1==x2 && x2==x3 && x3==x4 => // recurse
}

有没有更优雅的方法可以做到这一点?


一些注意事项:是的,我只是在学习 scala,如果不清楚,所以我不确定这是我真正想要做的事情,但我'我只是对可能的感兴趣。在这方面,我并不是真的在寻找完全不同的解决方案,例如 takeWhilefilter 之类的,但我对模式匹配特别感兴趣。

【问题讨论】:

    标签: scala pattern-matching


    【解决方案1】:

    Scala 的匹配并没有提供那么大的灵活性(这可能是一件好事,因为人们必须意识到无意的变量重用所导致的错误)。

    如果您有大量相同的项目,您可能需要考虑嵌套匹配(但请注意,您不会在内部匹配中失败,稍后在外部匹配中完成,因此您必须处理所有事情本地):

    list match {
      case x :: rest => rest match {
        case `x` :: `x` :: `x` :: xs => println("Four of the same")
        case _ => println("Well, nonempty at least")
      }
      case _ => println("Boring, there's nothing here!")
    }
    

    注意反引号的意思是“我们已经得到了这个变量,检查它,不要设置它!”。

    或者,如果您有重复使用的专门功能,您可以创建自定义匹配器:

    object FourOf {
      def unapplySeq(xs: List[Int]): Option[(Int, List[Int])] = xs match {
        case x :: y :: z :: a :: rest if x==y && y==z && z==a => Some((x,rest))
        case _ => None
      }
    }
    

    然后在您需要复杂模式时使用它:

    list match {
      case FourOf(x,rest) => println("four of the same")
      case x :: more => println("Nonempty")
      case _ => println("Yawn")
    }
    

    这些都不像您显然希望的那样整洁和灵活,但话又说回来,我不确定在匹配语句中分配和测试 同一个变量之间切换是无论如何编写清晰代码的好方法。

    【讨论】:

    • 谢谢!我没想到会有完全干净的东西,但只是对其他选择很感兴趣,所以这是完美的!我以前根本不知道反引号运算符。
    • 实际上,反引号操作符的意思是“字面意思是里面有什么”,可用于将任何内容作为变量名括起来,例如`def`。模式匹配分配以小写字母开头的变量,而反引号不是小写字母,所以它就是这样工作的。
    【解决方案2】:

    对于多次重复,您可以使用稳定的标识符进行比较(而不是捕获值):

    val x = list.head 
    list match {
      case `x`::`x`::`x`::`x`::xs => ....
    }
    

    但请注意,这不适用于空列表(您无法掌握它)。

    【讨论】:

    • 郑重声明,我完全尊重 om 与 Rex 对抗。
    • 不错的答案。你错过了case吗?尝试进行编辑,但成功了。
    • @Faiz 是的,我错过了case。感谢您的关注
    【解决方案3】:

    我认为 Rex 的回答令人震惊。我是unapplySeq 的粉丝。但是,如果您的主要麻烦只是每个守卫中== 的序列,这是一个不那么聪明且可能浪费的替代方案。 所以本着TMTOWTDI的精神:

    def same[A](xs: A*) = xs forall (xs.head==)
    
    // Then in your pattern match,
    
    list match {
      // case x1::x2::x3::x4::xs if x1==x2 && x2==x3 && x3==x4 => // recurse
      case x1::x2::x3::x4::xs if same(x1,x2,x3,x4) => // recurse
    }
    

    我也喜欢 Om 的回答,所以这里是一个改编:

     list.headOption map (x => list match { 
       case `x`::`x`::`x`::`x`::xs => //...; 
       case _ => // ... 
     }) getOrElse {
       // do what you'd have done for an empty list...
     }
    

    【讨论】:

    • 感谢所有不同的想法!我根本没想过headOption
    • 中间的似乎没有,我认为您需要将 x 绑定到某物或首先制作一个 x val,如其他示例所示
    • 您的权利。这意味着,不幸的是,对于那个简洁性没有净收益....
    猜你喜欢
    • 2012-07-23
    • 2013-04-02
    • 1970-01-01
    • 2021-11-13
    • 1970-01-01
    • 2014-11-08
    • 1970-01-01
    • 2020-10-31
    • 1970-01-01
    相关资源
    最近更新 更多