在Scala中,能否匹配一个区间?

53

在Scala中,是否可以匹配一系列的值?

例如:

val t = 5
val m = t match {
    0 until 10 => true
    _ => false
}

如果 t 在0到10之间,m 将为 true,否则为false。当然,这个小技巧不起作用,但有没有办法实现类似的功能?

4
请注意,通过写作“0 until 10”,您表示的是0、1、2、...、9(包括0,但不包括10)。如果您想包括10,请使用“0 to 10”。 - Jesper
请参考相关的stackoverflow问题:如何在Scala中对范围进行模式匹配? - David J.
标题要求如何将类型为“Range”的值与多个可能性匹配,例如:“我是否有(0..5)或(1..6)?” - Raphael
val m = 0 until 10 contains t 实际上是相同的,但更短。它将为您提供真/假答案。如果您只需要布尔答案。 - Peter Perháč
5个回答

82

使用Range进行守卫:

val m = t match {
  case x if 0 until 10 contains x => true
  case _ => false
}

1
那Double呢?0.1和10.0。 - Oleg Abrazhaev
@OlegAbrazhaev 这个怎么样? - Alexander Azarov
@AlexanderAzarov 怎么实现呢?你能加一个双倍的例子吗?步骤将是0.1,而不是1。 - Oleg Abrazhaev
1
@OlegAbrazhaev 在 REPL 中输入:scala> 0.0 until 10.0 by 0.1 contains 4.0,返回res0: Boolean = true - Alexander Azarov

34
您可以使用守卫(guards):
val m = t match {
    case x if (0 <= x && x < 10) => true
    case _ => false
}

3
就性能而言,这个解决方案比@alexander-azarov的解决方案更好。那里需要初始化范围,然后进行范围扫描。特别是对于大范围来说,这可能会成为一个问题。 - Oosterman
1
Range.contains 被覆盖了,所以它不需要扫描任何东西!这仍然是一些额外的代码,但 Hotspot 应该可以轻松地内联和优化它。 - Alexey Romanov

4

根据这些定义:

  trait Inspector[-C, -T] {
    def contains(collection: C, value: T): Boolean
  }

  implicit def seqInspector[T, C <: SeqLike[Any, _]] = new Inspector[C, T]{
    override def contains(collection: C, value: T): Boolean = collection.contains(value)
  }

  implicit def setInspector[T, C <: Set[T]] = new Inspector[C, T] {
    override def contains(collection: C, value: T): Boolean = collection.contains(value)
  }

  implicit class MemberOps[T](t: T) {
    def in[C](coll: C)(implicit inspector: Inspector[C, T]) =
      inspector.contains(coll, t)
  }

您可以进行以下检查:
2 in List(1, 2, 4)      // true
2 in List("foo", 2)     // true
2 in Set("foo", 2)      // true
2 in Set(1, 3)          // false
2 in Set("foo", "foo")  // does not compile
2 in List("foo", "foo") // false (contains on a list is not the same as contains on a set)
2 in (0 to 10)          // true

所以您需要的代码将是:

因此,您需要的代码将是:

val m = x in (0 to 10)

3

以下是使用范围进行匹配的另一种方法:

val m = t match {
  case x if ((0 to 10).contains(x)) => true
  case _ => false
}

1
这个重复了@Alexander Azarov的答案。 - Glenn
t == 10 的匹配错误。 - Alexander Prokofyev

2

另一个选择是使用隐式将其添加到语言中,我为int和Range添加了两个变体。

object ComparisonExt {
  implicit class IntComparisonOps(private val x : Int) extends AnyVal {
    def between(range: Range) = x >= range.head && x < range.last
    def between(from: Int, to: Int) = x >= from && x < to
  }

}

object CallSite {
  import ComparisonExt._

  val t = 5
  if (t between(0 until 10)) println("matched")
  if (!(20 between(0 until 10))) println("not matched")
  if (t between(0, 10)) println("matched")
  if (!(20 between(0, 10))) println("not matched")
}

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