collectFirst

问题描述

我有一些对象的序列,我想收集第一个一个函数为其返回 Some() 的元素

现在,我的代码是这样工作的:

mySeq.collectFirst{
  case elem if doSmth(elem).nonEmpty => 
    (doSmth(elem).get,elem)
}

有没有办法:

  1. 重构它不调用 doSmth 两次?

  2. 更改此代码以记录 smth,以防 doSmth 返回 None

res.match {
  case Some(v) => v
  case None => log.info("Searching further...")//partial function doesn't match
}

我想说,主要问题是 - 是否可以创建一个匹配所有情况的 PartialFunction,但对于某些情况我们确实会产生一些副作用(在这种情况下为日志消息)并明确说明它不是匹配吗?

编辑:

我想知道,为什么下面的代码在我运行测试时会抛出错误(虽然不是编译错误

mySeq.collectFirst{
case elem => 
  val r: Option[Int] = doSmth(elem)
  r match {
    case Some(res) => res
  }
}

解决方法

是的,可能有一种方法可以不调用 doSmth 两次并记录空值,但这看起来很丑陋(您必须明确地将 PartialFunction 子类化,拼出 {{1} },并有一个 isDefined 成员来保存结果)。

总的来说,这对于 var 来说不是一个好的用例。只需做这样的事情:

collectFirst

或者,如果您像我一样,更喜欢将一系列简短的转换链接到一个整体函数中,这里有一个奇特的方法:

   mySeq
     .view
     .map(doSmth)
     .flatMap { 
       case None => 
         log("nope")
         None
       case x => x
     }.headOption
,

您可以为此使用自定义模式匹配器。这是一个带有 unapply 方法的对象,可用于模式匹配。

此代码将执行与原始代码相同的操作,但只会调用 doSmth 一次:

object MatchSomething {
  def unapply[T](elem: T): Option[(String,T)] =
    doSmth(elem).map(r => (r,elem))
}

mySeq.collectFirst {
  case MatchSomething((smth,elem)) =>
    (smth,elem)
}

您可以在 unapply 方法中记录失败的匹配:

object MatchSomething {
  def unapply[T](elem: T): Option[(String,T)] =
    doSmth(elem) match {
      case Some(r) =>
        Some((r,elem))
      case None =>
        println(s"$elem did not match,searching further")
        None
    }
}