2017-06-20 156 views
0

我發現前面的問題,我一直在尋找的論壇這個答案,How to find a matching element in a list and map it in as an Scala API method?斯卡拉應用功能上的列表元素返回一個值

// Returns Some(66) 
List(1, 2, 3) collectFirst { case i if (i * 33 % 2 == 0) => i * 33 } 

現在,如果我用一個函數代替if子句

// 
List(1, 2, 3) collectFirst { case i if (test(i) > 0) => test(i) } 

這個工作,但測試()將被評估兩次。是否有更好的解決方案將函數應用於列表並在滿足條件時返回結果(不必遍歷所有元素,也不需要兩次調用該函數(用於評估和返回值)

回答

2

?這樣的事情,也許 分解成兩個獨立的操作讓您保存/重用中間結果

List(1,2,3).iterator.map(test).find(_ > 0) 
+0

爲什麼'.iterator'? –

+0

@GabrielePetronella迭代器是懶惰的,因此,這種方式在第一次返回肯定結果之前,只會調用'test'多次。 如果沒有'.iterator','test'會在開始評估'.find'條件之前首先應用於列表的_all_元素。因此,如果列表中有1000個元素,並且'test'在第一個元素上返回正值,則不會有'.iterator'的10000個調用,但只有一個調用。 – Dima

+0

非常感謝你們倆。這有很大幫助。下面的包裝是一個很好的模式,以及使我很難從下面選擇:-) – Sven

1

你可以用你的函數在自定義提取:

def test(i: Int): Int = i - 1 

object Test { 
    def unapply(i: Int): Option[Int] = Some(test(i)) 
} 

scala> List(1, 10, 20) collectFirst { case Test(i) if i > 0 => i } 
res0: Option[Int] = Some(9) 

可以概括這個解決方案一個D構成的那種提取的一類:

case class Extract[T, U](f: T => U) { 
    def unapply(t: T): Option[U] = Some(f(t)) 
} 

scala> val Test2 = Extract(test) 
Test2: Extract[Int,Int] = Extract($$Lambda$1326/[email protected]) 

scala> List(1, 10, 20) collectFirst { case Test2(i) if i > 0 => i } 
res1: Option[Int] = Some(9) 

您也可以包裝成後衛的提取,以及:

case class ExtractWithGuard[T, U](f: T => U)(pred: U => Boolean) { 
    def unapply(t: T): Option[U] = { 
    val u = f(t) 
    if (pred(u)) Some(u) 
    else None 
    } 
} 

scala> val Test3 = ExtractWithGuard(test)(_ > 0) 
Test3: ExtractWithGuard[Int,Int] = ExtractWithGuard($$Lambda$1327/[email protected]) 

scala> List(1, 10, 20) collectFirst { case Test3(i) => i } 
res2: Option[Int] = Some(9) 
+0

我也喜歡這個! – Sven