Scala在列表元素上应用函数以返回值

Scala在列表元素上应用函数以返回值,scala,list,function,Scala,List,Function,我在早些时候的论坛上找到了我一直在寻找的一个问题的答案 现在,如果我用一个函数替换if子句 // List(1, 2, 3) collectFirst { case i if ( test(i) > 0) => test(i) } 这是可行的,但test()将被计算两次。是否有更好的解决方案将函数应用于列表,并在满足条件时返回结果(不必遍历所有元素,也不必调用函数两次(用于求值和返回值)。可能是类似的情况? 将其分解为两个单独的操作可以保存/重用中间结果 List(1,2,3).

我在早些时候的论坛上找到了我一直在寻找的一个问题的答案

现在,如果我用一个函数替换if子句

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

这是可行的,但test()将被计算两次。是否有更好的解决方案将函数应用于列表,并在满足条件时返回结果(不必遍历所有元素,也不必调用函数两次(用于求值和返回值)。

可能是类似的情况? 将其分解为两个单独的操作可以保存/重用中间结果

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

您可以将函数包装在自定义提取器中:

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)
您可以概括此解决方案,并为此类提取器创建一个类:

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/1843609566@69c33ea2)

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/391731126@591a4d25)

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

为什么
.iterator
?@GabrielePetronella迭代器是懒惰的,因此,在第一次返回阳性结果之前,它只调用
test
所需的次数。如果没有
.iterator
test
将首先应用于列表中的所有元素,在
之前。find
条件将艺术得到评估。因此,如果一个列表有1000个元素,并且
test
在第一个元素上返回一个正值,那么在没有
.iterator
的情况下将有10000次调用,但只有一次调用。非常感谢你们两位。这非常有帮助。下面的包装器是一个很好的模式,也让我很难从中选择:-)同时,我能够通读文档,找到了一个懒惰列表流,并提出了这个解决方案:Stream(1,2,3).map(test).find(>0)@Sven迭代器比streams对内存更友好。如果您还没有一个列表,就像在您的示例中一样,这与其他方式无关,但是如果
列表
作为参数给定给函数,那么在遍历之前将其复制到
比简单地遍历
itera更昂贵tor
。我也喜欢这个!
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/391731126@591a4d25)

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