一个优雅的Scala解决方案如何找到一个匹配任何一组条件的元素,但每个条件具有不同的优先级?
case class A(p1:Int, p2:Int)
List(A(2,3), A(4,4), A(3,5))
说我想查询:找到p1 == p2
的项目,如果不这样做
找到它,给我p2 > p1
项,如果找不到,请给p1 = square(p2)
等等。始终=> Bool
我的第一个猜测是递归查找函数,它在每个循环中尝试一个条件,但我想知道是否有更好/更简单的方法来做到这一点。
理想情况下,如果项目符合更多条件,则应选择该项目,但仍然尊重条件优先权。
答案 0 :(得分:6)
l.find{x => x.p1 == x.p2}.getOrElse(l.maxBy(_.p2))
更新:在您更新的问题中,您说“给我带有最大p2的项目,如果找不到,请给出最低的p1,依此类推”。但是根据定义,总有一个最大的值(除非列表为空,但我收集的列表不能为空)。所以这真的只是“给我最大的p2项”(因此上面的解决方案)。
更新2 :所以问题再次改变了,现在是“找到p1 == p2的项目,如果找不到,请给我带p2> p1的项目,如果你找不到它,给p1 = square(p2)等等。 我猜你真的需要一个通用的解决方案。 所以这是我的看法:
// The list of predicates, in order of priority
// (you can add/remove predicates as you see fit)
val predicates = List[A => Boolean](
x => x.p1 == x.p2,
x => x.p2 > x.p1,
x => x.p1 == x.p2*x.p2
)
val indexedPredicates = predicates.reverse.zipWithIndex
def score( x: A ): Option[Int] = indexedPredicates.find(_._1(x)).map(_._2)
def priorityFind( l: List[A] ): A = l.maxBy(score)
这个想法是你将得分归因于每个元素:如果元素与任何谓词不匹配则为None;如果匹配最后一个谓词,则为Some(0);如果匹配最后一个谓词,则为Some(1),以及等等。然后你只需拿一个分数最高的那个(None
比任何Some
实例“更小”,这样就符合我们的要求。
如果你想正确处理没有元素匹配任何谓词的情况,你可以像这样更改priorityFind
:
def priorityFind( l: List[A] ): Option[A] = {
val filtered = l.view.flatMap{x => score(x).map(x -> _) }
if ( filtered.isEmpty ) None
else Some( filtered.maxBy(_._2)._1 )
}
答案 1 :(得分:1)
list.find(i => i.p1 == i.p2).getOrElse(list.sortBy(-_.p2).head)
更新,应符合您的新要求。
list.find(i => i.p1 == i.p2).getOrElse(
list.find(i => i.p2 > i.p1).getOrElse(
list.find(i => i.p1 == i.p2 * i.p2)))
答案 2 :(得分:0)
好的,我想出了这个:
case class A(p1:Int, p2:Int)
val list = List(A(3,2), A(4,4), A(10,2), A(4,2))
val filter:PartialFunction[A,A] = {
case a@A(p1,p2) if p1==p2 => a
case a@A(p1,p2) if p2>p1 => a
case a@A(p1,p2) if p1==p2*p2 => a
}
list.collect(filter).foreach(println)
现在,如果你需要最合适的话,我会做一个排名函数,而不是映射A => Int然后按它排序。您可以先过滤后再保存一些作品,然后再按照排名进行排序。 (collect采用list.size时间,而排序平均需要list.size * log(list.size))