我有一个要过滤的列表中的结果。
用户可以为行上的任何属性提供特定限制(例如,我只想查看 x == 1 的行)。如果他们没有指定限制,那么当然不会使用该谓词。当然,最简单的形式是:
list.filter(_.x == 1)
有许多可能的简单谓词,我正在使用将用户搜索词(例如 Option[Int])转换为谓词函数或 Identity(返回 true 的函数)的代码即时构建一个新的谓词函数。代码看起来像这样(缩短,为清楚起见添加了显式类型):
case class ResultRow(x: Int, y: Int)
object Main extends App {
// Predicate functions for the specific attributes, along with debug output
val xMatches = (r: ResultRow, i: Int) => { Console println "match x"; r.x == i }
val yMatches = (r: ResultRow, i: Int) => { Console println "match y"; r.y == i }
val Identity = (r : ResultRow) => { Console println "identity"; true }
def makePredicate(a: Option[Int], b: Option[Int]) : ResultRow => Boolean = {
// The Identity entry is just in case all the optional params are None
// (otherwise, flatten would cause reduce to puke)
val expr = List(Some(Identity),
a.map(i => xMatches(_: ResultRow, i)),
b.map(i => yMatches(_: ResultRow, i))
).flatten
// Reduce the function list into a single function.
// Identity only ever appears on the left...
expr.reduceLeft((a, b) => (a, b) match {
case (Identity, f) => f
case (f, f2) => (r: ResultRow) => f(r) && f2(r)
})
}
val rows = List(ResultRow(1, 2), ResultRow(3, 100))
Console println rows.filter(makePredicate(Some(1), None))
Console println rows.filter(makePredicate(None, None))
Console println rows.filter(makePredicate(None, Some(100)))
Console println rows.filter(makePredicate(Some(3), Some(100)))
}
这完美地工作。运行时,它会正确过滤,并且调试输出证明调用了最少数量的函数来正确过滤列表:
match x
match x
List(ResultRow(1,2))
identity
identity
List(ResultRow(1,2), ResultRow(3,100))
match y
match y
List(ResultRow(3,100))
match x
match x
match y
List(ResultRow(3,100))
实际上,我对结果如此之好感到非常满意。
但是,我不禁认为有一种更实用的方法可以做到这一点(例如 Monoids 和 Functors 和广义求和)......但我不知道如何让它工作。
我尝试遵循一个 scalaz 示例,该示例表明我需要创建一个隐式零和半群,但我无法让 Zero[ResultRow => Boolean] 进行类型检查。