Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a function that returns the logical OR of several boolean predicates

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?

like image 531
Mark T. Avatar asked Dec 11 '22 18:12

Mark T.


2 Answers

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") 
like image 84
dhg Avatar answered Feb 22 '23 23:02

dhg


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
like image 31
Nate Nystrom Avatar answered Feb 23 '23 00:02

Nate Nystrom