在Scala中查找函数应用值

时间:2011-11-25 00:30:31

标签: scala collections scala-collections

我有一个问题,我一直在努力寻找使用现有Scala集合库的最佳解决方案,但我似乎无法想出一些东西。

给定一组函数,我需要找到满足谓词的某些输入的第一个函数结果。这是一个简单的实现:

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean): Option[B] = {
  var result: Option[B] = None
  breakable {
    for (e <- t) {
      val r = e(value)
      if (p(r)) { result = Some(r); break }
    }
  }
  result
}


// test
val f1 = (s: String) => if (s == "a") "aa" else null
val f2 = (s: String) => if (s == "b") "bb" else null
val l = List(f1, f2)

findResult(l, "b", (v: Any) => v != null) must equal(Some("bb"))

使用Collections API有更好的方法吗?

编辑:我想要实施的一个限制是每个函数只应该应用一次,因为虽然我的例子很简单,但我的实际用法并非如此。这个限制促成了我上面的实现。

3 个答案:

答案 0 :(得分:4)

我只是对tenshi的答案发表评论,但后来我决定将其扩展为另一种方法。请注意,如果您在严格的Traversable上使用map,那么在任何发现之前,整个列表将被映射到。这意味着你最终会做一些额外的工作。

您只需使用查找:

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean) =
    t find (fn => p(fn(value)))

这将返回满足p的谓词value函数。如果您需要结果,则只需要将该函数再次应用于该值(假设该函数是引用透明的)。当然,这将会执行一些额外的工作,但可能比tenshi的技术稍微减少额外的工作量。请注意,您自己提出的技术可以执行 no 额外工作。

[更新]如果确实不想执行任何额外工作,那么您应该使用collection view。我不得不看一下,但我想我已经掌握了它。现在,直接窃取tenshi的代码并添加.view,这是我的交互式会话中的一些copypasta:

def f1(x: Int): Int = { println("f1"); x }
f1: (x: Int)Int
def f2(x: Int): Int = { println("f2"); x+1 }
f2: (x: Int)Int
def f3(x: Int): Int = { println("f3"); x+2 }
f3: (x: Int)Int
val fs = List(f1 _, f2 _, f3 _)
fs: List[(Int) => Int] = List(, , )
(fs.view map (f => f(1))) find (_ == 2)
f1
f2
res8: Option[Int] = Some(2)

如您所见,f1和f2已执行,但未执行f3。这是因为一旦f2(1)的结果被发现为== 2find函数就能够停止。这是视图魔力的一部分:懒惰映射。实际上,mapfind操作由于视图而融合在一起!或者我被告知。

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean) =
    t.view map (f => f(value)) find p
def even(x: Int) = x % 2 == 0

findResult(fs, 1, even)
f1
f2
res13: Option[Int] = Some(2)

所以你有它。我在上面链接的文档中找到的一个宝石就是:

  

[从Scala 2.8开始]除了流和视图之外的所有集合都是严格的。从严格到惰性集合的唯一方法是使用view方法。返回的唯一方法是通过force

答案 1 :(得分:2)

mapfind的组合应该有效:

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean) =
    t map (fn => fn(value)) find p

答案 2 :(得分:2)

您可以使用view

def findResult[A, B](t: Traversable[Function1[A, B]], value: A, p: B => Boolean) = {
  t.view.map(_(value)).find(p(_))
}