フィルタリングしたい結果がリストにあります。
ユーザーは、行の任意の属性に特定の制限を指定できます(たとえば、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))
私は実際、これがどれほどうまくいったかについて非常に満足しています。
しかし、私はそれを行うためのより機能的な方法があると思わずにはいられません(例えば、モノイドとファンクターと一般化された合計)...しかし、それを機能させる方法を理解することはできません。
暗黙のゼロと半群を作成する必要があることを示すscalazの例に従ってみましたが、タイプチェックするためにZero [ResultRow=>Boolean]を取得できませんでした。