在Scala中组合find和instanceof的任何干净方法?

时间:2011-04-15 15:27:30

标签: scala functional-programming

我想在一些Iterable中找到一些符合某些给定类型的元素,并验证以该类型为参数的谓词。

我使用命令式编程编写了这个方法,这似乎符合我的期望。有没有办法用更“scalaesque”的方式写这个?

def findMatch[T](it: Iterable[_], clazz: Class[T], pred: T => Boolean): Option[T] = {
  val itr = it.iterator
  var res: Option[T] = None
  while (res.isEmpty && itr.hasNext) {
    val e = itr.next()
    if (clazz.isInstance(e) && pred(clazz.cast(e))) {
      res = Some(clazz.cast(e))
    }
  }
  res
}

4 个答案:

答案 0 :(得分:17)

如果您想要collect,然后find,则可以使用map

scala> val it: Iterable[Any] = List(1,2,3,"4")            
it: Iterable[Any] = List(1, 2, 3, 4)

scala> it.view.collect{case s: String => s}.headOption
res1: Option[String] = Some(4)

答案 1 :(得分:1)

您可以使用现有类型X forSome{typeX},而不是使用_作为类型参数。然后,这将使您能够使用提到的find方法编写它,并在Option类型上使用map方法:

def findMatch[T](it: Iterable[X forSome {type X}], clazz: Class[T], pred: T => Boolean): Option[T] = {
    it.find{ e => clazz.isInstance(e) && pred(clazz.cast(e))}.map{clazz.cast(_)}
}

答案 2 :(得分:1)

如果将问题划分为子问题,则很容易找到更惯用的版本。 你想要

  1. T
  2. 中查找Iterable[Any]的所有实例
  3. 将它们转换为T以使编译器满意
  4. 找到第一个匹配的元素
  5. 首先,您可以在filter上轻松使用Iterator方法。你有

    it.iterator.filter(x => clazz.isInstance(x))
    

    会返回仅包含Iterator[Any]的{​​{1}}。现在让我们说服编译器:

    T

    好的,现在你有一个it.iterator.filter(x => clazz.isInstance(x)).map(x => x.asInstanceOf[T]) - 所以你只需要找到第一个满足谓词的元素:

    Iterator[T]

答案 3 :(得分:0)

您可以使用Iterable的{​​{1}}方法并与警卫进行模式匹配:

find

有关模式匹配的介绍,请参阅:  http://programming-scala.labs.oreilly.com/ch03.html