Reputation: 567
Suppose I have in my code a number of boolean predicates defined:
def pred1[A](x: A): Boolean = { ... }
def pred2[A](x: A): Boolean = { ... }
def pred3[A](x: A): Boolean = { ... }
Now, I'd like to be able to create a function that is, for example, the logical OR of pred1
and pred3
.
So, something like:
def pred1Or3[A](x: A) = or(pred1, pred2)
Even better, it'd be nice to be able to generalize so that I could provide my own combining function. So, if instead, I wanted to have the logical AND, I'd call:
def pred1And3[A](x: A) = combine({_ && _}, pred1, pred2)
I can achieve the same basic effect this way:
def pred1And3[A](x: A) = Seq(pred1, pred2) map { _(x) } reduce { _ && _ }
but that seems a little verbose and clouds the intent. Is there a simpler way to do this in Scala?
Upvotes: 4
Views: 1588
Reputation: 28676
Here's one solution that fails fast, but isn't as generic:
def or[A](ps: (A => Boolean)*) = (a:A) => ps.exists(_(a))
def and[A](ps: (A => Boolean)*) = (a:A) => ps.forall(_(a))
scala> def or[A](ps: (A => Boolean)*) = (a:A) => ps.exists(_(a))
or: [A](ps: A => Boolean*)A => Boolean
scala> or((x:Int) => {println("a");x > 5}, (x:Int) => {println("b");x < 2})
res6: Int => Boolean = <function1>
scala> res6(1)
a
b
res7: Boolean = true
scala> res6(6)
a
res8: Boolean = true
Upvotes: 2
Reputation: 475
Here's a solution that I've used in the past:
implicit def wrapPredicates[A](f: A => Boolean) = new {
def <|>(g: A => Boolean) = (x: A) => f(x) || g(x)
def <&>(g: A => Boolean) = (x: A) => f(x) && g(x)
}
Use as follows:
val pred12or3 = pred1 <|> pred2 <|> pred3
Upvotes: 4
Reputation: 52701
Here's a solution that is simple and allows a variable number of items to be passed at the same time. I've given both the or
case and the more generic combine
case:
def or[A](ps: (A => Boolean)*) =
(a: A) => ps.exists(_(a))
def combine[A](ps: (A => Boolean)*)(op: (Boolean, Boolean) => Boolean) =
(a: A) => ps.map(_(a)).reduce(op)
Here's some example usage:
// "or" two functions
val pred1or3 = or(pred1, pred3)
pred1or3("the")
// "or" three functions
val pred12or3 = or(pred1, pred2, pred3)
pred12or3("the")
// apply a dijoined rule directly
or(pred1, pred2, pred3)("the")
// combine two functions with "and"
val pred12and3 = combine(pred1, pred3)(_ && _)
pred12and3("the")
// apply a conjoined rule directly
combine(pred1, pred2, pred3)(_ && _)("the")
// stack functions as desired (this is "(pred1 || pred3) && (pred1 || pred2)")
combine(or(pred1, pred3), or(pred1, pred2))(_ && _)("a")
Upvotes: 6
Reputation: 167911
def or[A](p: A => Boolean, q: A => Boolean) = (a: A) => p(a) || q(a)
def logic[A](p: A => Boolean, q: A => Boolean)(c: (Boolean, Boolean) => Boolean) = {
(a: A) => c( p(a) , q(a) )
}
You could add a parameter (a: A)
to these methods instead of returning a function, e.g.:
def or2[A](a: A)(p: A => Boolean, q: A => Boolean) = p(a) || q(a)
Upvotes: 1