我有一个问题,我一直在努力寻找使用现有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有更好的方法吗?
编辑:我想要实施的一个限制是每个函数只应该应用一次,因为虽然我的例子很简单,但我的实际用法并非如此。这个限制促成了我上面的实现。
答案 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)
的结果被发现为== 2
,find
函数就能够停止。这是视图魔力的一部分:懒惰映射。实际上,map
和find
操作由于视图而融合在一起!或者我被告知。
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)
map
和find
的组合应该有效:
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(_))
}