1

我希望在 List 中找到匹配项并根据匹配项返回值。CollectFirst 适用于匹配集合的元素,但在这种情况下,我想匹配元素的成员 swEl 而不是元素本身。

abstract class CanvNode (var swElI: Either[CSplit, VistaT])
{         
  private[this] var _swEl: Either[CSplit, VistaT] = swElI
  def member = _swEl
  def member_= (value: Either[CSplit, VistaT] ){ _swEl = value; attach}
  def attach: Unit
  attach

  def findVista(origV: VistaIn): Option[Tuple2[CanvNode,VistaT]] = member match
  {
    case Right(v) if (v == origV) => Option(this, v) 
    case _ => None
  }
}

def nodes(): List[CanvNode] = topNode :: splits.map(i => List(i.n1, i.n2)).flatten

//Is there a better way of implementing this? 
val temp: Option[Tuple2[CanvNode, VistaT]] = 
  nodes.map(i => i.findVista(origV)).collectFirst{case Some (r) => r}

我是否需要对此进行查看,或者 collectFirst 方法会确保仅根据需要创建集合?

我觉得这一定是一个相当普遍的模式。另一个例子是,如果一个人有一个主 List 元素的 List 成员,并且想要返回第四个元素(如果有的话)。有没有我可以调用的标准方法?如果失败,我可以创建以下内容:

implicit class TraversableOnceRichClass[A](n: TraversableOnce[A])
{
  def findSome[T](f: (A) => Option[T]) = n.map(f(_)).collectFirst{case Some (r) => r}
}    

然后我可以将上面的内容替换为:

val temp: Option[Tuple2[CanvNode, VistaT]] = 
  nodes.findSome(i => i.findVista(origV))

这使用 2.10 中的隐式类,用于 2.10 之前的使用:

class TraversableOnceRichClass[A](n: TraversableOnce[A])
{
  def findSome[T](f: (A) => Option[T]) = n.map(f(_)).collectFirst{case Some (r) => r}
}

implicit final def TraversableOnceRichClass[A](n: List[A]):
  TraversableOnceRichClass[A] = new TraversableOnceRichClass(n)
4

1 回答 1

1

作为介绍性侧节点:您正在描述的操作(Some如果存在则返回第一个,否则返回)是s 的“第一个”幺半群实例下的 sNone集合的总和。例如,使用Scalaz 6OptionOption

scala> Stream(None, None, Some("a"), None, Some("b")).map(_.fst).asMA.sum
res0: scalaz.FirstOption[java.lang.String] = Some(a)

或者,您可以将这样的内容放在范围内:

implicit def optionFirstMonoid[A] = new Monoid[Option[A]] {
  val zero = None
  def append(a: Option[A], b: => Option[A]) = a orElse b
}

并跳过这一.map(_.fst)部分。不幸的是,这些方法在 Scalaz 中都没有适当的惰性,因此将评估整个流(与 Haskell 不同,mconcat . map (First . Just) $ [1..]例如 where 就可以了)。


编辑:作为旁注的旁注:显然Scalaz确实提供了一个sumr适当的惰性(对于流-这些方法都不适用于视图)。因此,例如,您可以这样写:

Stream.from(1).map(Some(_).fst).sumr

不要永远等待你的答案,就像在 Haskell 版本中一样。


但假设我们坚持使用标准库,而不是这样:

n.map(f(_)).collectFirst{ case Some(r) => r }

我会写以下内容,这或多或少是等效的,并且可以说更惯用:

n.flatMap(f(_)).headOption

例如,假设我们有一个整数列表。

val xs = List(1, 2, 3, 4, 5)

我们可以使它变得惰性,并map在其上添加一个具有副作用的函数,以在访问其元素时向我们显示:

val ys = xs.view.map { i => println(i); i }

现在我们可以在结果集合上使用flatMap一个Option-returning 函数,并使用headOption它(安全地)返回第一个元素,如果它存在的话:

scala> ys.flatMap(i => if (i > 2) Some(i.toString) else None).headOption
1
2
3
res0: Option[java.lang.String] = Some(3)

很明显,当我们按需要达到非空值时,这种情况就会停止。是的,如果您的原始集合是严格的,您肯定需要一个视图,因为否则headOption(or collectFirst) 无法返回并停止它之前的flatMap(or )。map

在您的情况下,您可以跳过findVista并使用以下内容更加简洁:

val temp = nodes.view.flatMap(
  node => node.right.toOption.filter(_ == origV).map(node -> _)
).headOption

当然,您是否觉得这更清晰或只是一团糟,这是一个品味问题。

于 2012-08-29T23:19:47.750 回答