2017-06-20 39 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