Scala:在集合中查找匹配和返回匹配值的通用方法

时间:2012-08-29 22:22:28

标签: scala scala-collections

我希望在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}

我是否需要View,或者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)

1 个答案:

答案 0 :(得分:1)

作为介绍性的侧节点:您正在描述的操作(如果存在,则返回第一个Some,否则返回None)是Option下的Option集合的总和scala> Stream(None, None, Some("a"), None, Some("b")).map(_.fst).asMA.sum res0: scalaz.FirstOption[java.lang.String] = Some(a) 的“第一个”monoid实例。例如,使用Scalaz 6

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

或者你可以把这样的东西放在范围内:

.map(_.fst)

跳过mconcat . map (First . Just) $ [1..]部分。遗憾的是,这些方法在Scalaz中都没有适当的延迟,因此将对整个流进行评估(例如,与Haskell不同,sumr就好了)。


编辑:作为旁注的旁注:显然Scalaz 提供了适当懒惰的Stream.from(1).map(Some(_).fst).sumr (对于流 - 这些方法都不适用于视图)。例如,你可以这样写:

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

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


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

n.flatMap(f(_)).headOption

我会写下以下内容,或多或少相当,并且可以说更具惯用性:

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

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

map

我们可以使这个懒惰的val ys = xs.view.map { i => println(i); i } 一个带有副作用的函数向我们展示何时访问它的元素:

flatMap

现在我们可以Option 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

很明显,当我们按照需要点击非空值时会停止。是的,如果您的原始集合是严格的,您肯定需要一个视图,否则collectFirst(或flatMap)无法返回并停止map(或{{1在它之前的那个。

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

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

当然,无论你发现这个更清楚还是一团糟都是品味问题。