【问题标题】:Can a range be matched in Scala?可以在Scala中匹配范围吗?
【发布时间】:2010-11-23 16:27:36
【问题描述】:

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

例如:

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

如果t 介于 0 和 10 之间,m 将是 true,否则为 false。这一点当然是行不通的,但是有没有什么办法可以实现呢?

【问题讨论】:

  • 请注意,写“0 到 10”是指 0、1、2、...、9(包括 0,不包括 10)。如果要包括 10,请使用“0 到 10”。
  • 查看相关的 stackoverflow 问题:How can I pattern match on a range in Scala?
  • 标题询问如何将Range 类型的值与多种可能性相匹配,例如“我有(0..5) 还是(1..6)?”
  • val m = 0 until 10 contains t 实际上相同但更短。它会给你正确/错误的答案。如果您只需要一个布尔答案。

标签: scala pattern-matching range matching


【解决方案1】:

使用Range保护:

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

【讨论】:

  • 双倍呢? 0.1 和 10.0
  • @OlegAbrazhaev 这是怎么回事?
  • @AlexanderAzarov 如何实现呢?你能添加一个双打的例子吗?步长将是 0.1,而不是 1。
  • @OlegAbrazhaev 在 REPL 中:scala> 0.0 until 10.0 by 0.1 contains 4.0,返回 res0: Boolean = true
【解决方案2】:

你可以使用守卫:

val m = t match {
    case x if (0 <= x && x < 10) => true
    case _ => false
}

【讨论】:

  • 就性能而言,该解决方案优于@alexander-azarov 解决方案。需要初始化范围,然后进行范围扫描。尤其是对于大范围,这可能会成为一个问题。
  • Range.contains 当然被覆盖了,所以它不需要扫描任何东西!这仍然是一些额外的代码,但 Hotspot 应该内联和优化它没有问题。
【解决方案3】:

使用这些定义:

  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)

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

    • 这重复了@Alexander Azarov 的回答。
    • t == 10 匹配错误。
    【解决方案5】:

    另一种选择是使用隐式将其实际添加到语言中,我为 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")
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-02
      • 2011-03-10
      • 1970-01-01
      • 2018-06-14
      • 2015-07-10
      • 2018-01-04
      相关资源
      最近更新 更多