【问题标题】:Scala apply function on list elements to return a valueScala 在列表元素上应用函数以返回值
【发布时间】:2017-11-23 04:58:03
【问题描述】:

我之前在论坛中找到了我一直在寻找的问题的答案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) }

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

【问题讨论】:

    标签: scala list function


    【解决方案1】:

    大概是这样的吧? 将其分解为两个单独的操作可以让您保存/重用中间结果。

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

    【讨论】:

    • 为什么.iterator
    • @GabrielePetronella 迭代器是惰性的,因此,在第一次返回肯定结果之前,它只会调用test 所需的次数。如果没有.iteratortest 将首先应用于列表的所有 元素,然后.find 条件将开始被评估。因此,如果一个列表有 1000 个元素,而 test 在其中的第一个元素上返回一个正值,那么将有 10000 次没有 .iterator 的调用,但只有一个。
    • 非常感谢你们俩。这很有帮助。下面的包装器是一个很好的模式,也让我很难从中选择:-)
    • 同时我能够通读文档并找到 Stream 这是一个惰性列表并提出了这个解决方案: Stream(1,2,3).map(test).find(_ > 0)
    • @Sven 迭代器比流对内存更友好。如果您没有已经有一个列表,就像在您的示例中一样,那么无论哪种方式都没有关系,但是如果 List 被例如作为参数提供给函数,在遍历之前将其复制到Stream 比简单地通过iterator 更昂贵。
    【解决方案2】:

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

    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)
    

    【讨论】:

    • 我也喜欢这个!
    猜你喜欢
    • 2022-01-11
    • 1970-01-01
    • 2013-12-08
    • 2022-12-05
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多