1

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

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

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

4

3 回答 3

6
l.find{x => x.p1 == x.p2}.getOrElse(l.maxBy(_.p2))

更新:在您更新的问题中,您说“给我 p2 最大的项目,如果找不到,请给我最低 p1 等等”。但是根据定义,总是有一个最大值(除非列表为空,但我认为列表不能为空)。所以这实际上只是“给我最大 p2 的项目”(因此上述解决方案)。

更新 2:所以问题又变了,现在是“找到 p1 == p2 的项目,如果找不到,请给我 p2 > p1 的项目,如果找不到,请给 p1 =方(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 )
}
于 2013-03-05T21:01:08.413 回答
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))) 
于 2013-03-05T20:57:06.643 回答
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 然后按它排序。您可以通过先过滤然后再根据您的排名进行排序来节省一些工作。(收集需要 list.size 时间,而排序平均需要 list.size * log(list.size))

于 2013-03-05T21:29:22.513 回答