有什么干净的方法可以在 Scala 中结合 find 和 instanceof 吗?

发布于 2024-11-02 06:28:17 字数 465 浏览 4 评论 0原文

我想在一些 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
}

I want to find in some Iterable some elements that both conform to some given type, and validates a predicate taking that type as an argument.

I wrote this method using imperative-style programming, which seems to conform to my expectations. Is there some way to write this in a more "scalaesque" way?

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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

世界等同你 2024-11-09 06:28:17

如果您想查找,然后映射,您可以使用collect

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)

You can use collect if you want to find and then 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)
绿光 2024-11-09 06:28:17

您可以使用存在类型 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(_)}
}

You can work with an existantial type X forSome{typeX} rather than using _ as type parameter. This then would enable you to write it with the mentioned find method and use the map method on the Option type:

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(_)}
}
时光磨忆 2024-11-09 06:28:17

如果您将问题划分为子问题,则很容易找到更惯用的版本。
您希望

  1. Iterable[Any] 中查找 T 的所有实例,
  2. 将它们强制转换为 T 以使编译器很乐意
  3. 找到第一个匹配元素

For第一点,您可以轻松地在 Iterator 上使用 filter 方法。因此,您将

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

返回一个仅包含 TIterator[Any] 。现在让我们说服编译器:

it.iterator.filter(x => clazz.isInstance(x)).map(x => x.asInstanceOf[T])

好的,现在你有了一个 Iterator[T] ,所以你只需要找到满足你的谓词的第一个元素:

def findMatch[T](it: Iterable[Any], clazz: Class[T], pred: T => Boolean): Option[T] = 
  it.iterator.filter(x => clazz.isInstance(x))
             .map(x => x.asInstanceOf[T])
             .find(pred)

If you divide your problem into subproblems a more idiomatic version is easy to find.
You want to

  1. find all instances of T in your Iterable[Any]
  2. cast them to T to make the compiler happy
  3. find the first matching element

For the first point you can easily use the filter Method on Iterator. So you have

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

which returns you an Iterator[Any] that contains only Ts. Now let's convince the compiler:

it.iterator.filter(x => clazz.isInstance(x)).map(x => x.asInstanceOf[T])

Okay, now you have an Iterator[T] - so you just need to find the first element fulfilling your predicate:

def findMatch[T](it: Iterable[Any], clazz: Class[T], pred: T => Boolean): Option[T] = 
  it.iterator.filter(x => clazz.isInstance(x))
             .map(x => x.asInstanceOf[T])
             .find(pred)
往事风中埋 2024-11-09 06:28:17

您可以使用 Iterablefind 方法和带有守卫的模式匹配:

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

scala> it.find { _ match {
  case s: String if s == "4" => true
  case _ => false
}}.asInstanceOf[Option[String]]
res0: Option[String] = Some(4)

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

You can use Iterable's find method and pattern matching with a guard:

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

scala> it.find { _ match {
  case s: String if s == "4" => true
  case _ => false
}}.asInstanceOf[Option[String]]
res0: Option[String] = Some(4)

For an introduction to pattern matching have a look at:
http://programming-scala.labs.oreilly.com/ch03.html

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文