在一个模式中多次使用相同的变量

时间:2012-12-20 00:30:56

标签: scala pattern-matching

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

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

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

在研究这个问题时,我发现我还可以在案例条款中包含一名警卫,所以我可以做到

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或其他什么,但更多的是对模式匹配特别感兴趣。

3 个答案:

答案 0 :(得分:8)

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

这些都不像你显然希望的那样整洁和灵活,但是再一次,我不确定在匹配语句中分配和测试相同的变量之间的翻转是一个无论如何,编写清晰代码的好方法。

答案 1 :(得分:5)

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

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

但请注意,这不适用于空列表(你只是无法得到它)。

答案 2 :(得分:3)

我认为雷克斯的答案很明显。我是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...
 }