2012-08-29 16 views
1

我希望在列表中找到匹配並返回取決於匹配的值。 CollectFirst適用於匹配集合中的元素,但在這種情況下,我想匹配元素的成員swEl而不是元素本身。Scala:查找匹配並在集合中返回匹配相關值的通用方法

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) 

回答

1

作爲介紹性節點:您正在描述的操作(如果存在,返回第一個Some,否則返回None)是01集合的總和s在Option的「第一個」monoid實例下。因此,例如,與Scalaz 6

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..]就好了)。


編輯:作爲一個側面說明這個側面說明:顯然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 } 

現在我們可以flatMapOption -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(或collectFirst)無法返回並停止之前的flatMap(或map)。

在你的情況,你可以跳過findVista並獲得更簡潔的東西是這樣的:

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

不管你覺得這更清晰,或只是一個混亂當然是口味的問題。

+0

headOption來自TraversableLike。 Pimping TraversableLike比PSPping TraversableOnce稍微複雜一點,所以在檢查答案之前我必須先熟練掌握。 –