如何根据不同的优先级条件找到元素?

时间:2013-03-05 20:52:01

标签: scala

一个优雅的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

我的第一个猜测是递归查找函数,它在每个循环中尝试一个条件,但我想知道是否有更好/更简单的方法来做到这一点。

理想情况下,如果项目符合更多条件,则应选择该项目,但仍然尊重条件优先权。

3 个答案:

答案 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))