问题
I was informed to use this interesting piece of code, but my use case requires it to do a bit more than is currently possible.
implicit class Predicate[A](val pred: A => Boolean) {
def apply(x: A) = pred(x)
def &&(that: A => Boolean) = new Predicate[A](x => pred(x) && that(x))
def ||(that: A => Boolean) = new Predicate[A](x => pred(x) || that(x))
def unary_! = new Predicate[A](x => !pred(x))
}
Some use case boilerplate:
type StringFilter = (String) => Boolean
def nameFilter(value: String): StringFilter =
(s: String) => s == value
def lengthFilter(length: Int): StringFilter =
(s: String) => s.length == length
val list = List("Apple", "Orange", "Meat")
val isFruit = nameFilter("Apple") || nameFilter("Orange")
val isShort = lengthFilter(5)
list.filter { (isFruit && isShort) (_) }
So far everything works ok. But say that I want to do something like this:
val nameOption: Option[String]
val lengthOption: Option[Int]
val filters = {
nameOption.map((name) =>
nameFilter(name)
) &&
lengthOption.map((length) =>
lengthFilter(length)
)
}
list.filter { filters (_) }
So now I need to &&
an Option[(A) => Boolean]
If the Option is None then simply disregard the filter.
If I use something like:
def const(res:Boolean)[A]:A=>Boolean = a => res
implicit def optToFilter[A](optFilter:Option[A => Boolean]):A => Boolean = optFilter match {
case Some(filter) => filter
case None => const(true)[A]
}
I have the problem of ||
with one filter set to true. I can solve this by changing true to false but then the same problem exists with &&
.
I could also take this approach:
implicit def optionalPredicate[A](pred: Option[A => Boolean]): OptionalPredicate[A] = new OptionalPredicate(pred)
class OptionalPredicate[A](val pred: Option[A => Boolean]) {
def apply(x: A) = pred match {
case Some(filter) => filter(x)
case None => trueFilter(x)
}
def &&(that: Option[A => Boolean]) = Some((x: A) =>
pred.getOrElse(trueFilter)(x) && that.getOrElse(trueFilter)(x))
def ||(that: Option[A => Boolean]) = Some((x: A) =>
pred.getOrElse(falseFilter)(x) || that.getOrElse(falseFilter)(x))
}
def trueFilter[A]: A => Boolean = const(res = true)
def falseFilter[A]: A => Boolean = const(res = false)
def const[A](res: Boolean): A => Boolean = a => res
But something seems inherently wrong with having to convert the Predicate into an OptionalPredicate when the predicate is not the child type of an Option:
implicit def convertSimpleFilter[A](filter: A => Boolean) = Some(filter)
Which allows:
ifFruit && isShort
I have the feeling that the Optional should tie in seamlessly with Predicate while following DRY principles.
回答1:
See below (UPD)
It would be good to be able to convert an Option[Filter]
to Filter
:
def const(res:Boolean)[A]:A=>Boolean = a => res
implicit def optToFilter[A](optFilter:Option[A => Boolean]):A => Boolean = optFilter match {
case Some(filter) => filter
case None => const(true)[A]
}
Then we would like to use any type that can be converted to a predicate:
implicit class Predicate[A, P <% A=>Boolean](val pred: P)
(and change usages of pred
to (pred:A=>Boolean)
)
def &&[P2:A=>Boolean](that: P2) = new Predicate[A](x => (pred:A=>Boolean)(x) && (that:A=>Boolean)(x))
UPD
Option[A=>Boolean]
will be the real filter type.
implicit def convertSimpleFilter[A](filter:A=>Boolean)=Some(filter)
implicit class Predicate[A](val pred: Option[A=>Boolean]){
def &&(that:Option[A=>Boolean]) = Some((x:A) => pred.getOrElse(const(true))(x) && that.getOrElse(const(true))(x) )
def ||(that:Option[A=>Boolean]) = Some((x:A) => pred.getOrElse(const(false))(x) || that.getOrElse(const(false))(x) )
}
来源:https://stackoverflow.com/questions/28659506/scala-optional-predicate