Поиск элементов в списке Scala, а также узнать, какой предикат был удовлетворен - PullRequest
10 голосов
/ 01 февраля 2010

У меня есть следующая проблема в Scala. Я должен найти первый элемент в списке al, который удовлетворяет функции предиката с двумя условиями в OR. Проблема в том, что я хотел бы получить элемент, но также знаю, какое из двух условий было выполнено. Вот простой пример:

val l1 = List("A", "B", "AA", "BB")
val l2 = List("AA", "BB", "A", "B")

def c1(s: String) = s.startsWith("B")
def c2(s: String) = s.length == 2

println(l1.find(s => c1(s) || c2(s)))
println(l2.find(s => c1(s) || c2(s)))

результат:

Some(B)
Some(AA)

Для случая l1 я хотел бы получить некоторое возвращаемое значение (например, String), указывающее, что c1 было выполнено (c2 для случая l2). Возможное решение может состоять в том, чтобы определить переменную перед тестом и установить ее в функциях c1 и c2, но я хотел бы найти более «функциональное» решение, возможно, такое, которое возвращает Tuple вроде: ).

Заранее спасибо за помощь

Ответы [ 3 ]

9 голосов
/ 01 февраля 2010

Я бы сделал это:

Scala 2.8:

def find2p[T](l: List[T], p1: T => Boolean, p2: T => Boolean) = 
  l.view.map(el => (el, p1(el), p2(el))).find(t => t._2 || t._3)

Scala 2.7:

def find2p[T](l: List[T], p1: T => Boolean, p2: T => Boolean) = 
  l.projection.map(el => (el, p1(el), p2(el))).find(t => t._2 || t._3)

view / projection гарантирует, что отображение будет выполнено по требованию, а не будет применено ко всему списку.

3 голосов
/ 01 февраля 2010
def find[T](l1 : List[T], c1 : T => Boolean, c2 : T => Boolean) = ((None : Option[(String, T)]) /: l1)( (l, n) => l match {
    case x : Some[_] => l
    case x if c1(n) => Some("c1", n)
    case x if c2(n) => Some("c2", n)
    case _ => None
})

scala> find(l1, c1, c2)
res2: Option[(String, java.lang.String)] = Some((c1,B))

scala> find(l2, c1, c2)
res3: Option[(String, java.lang.String)] = Some((c2,AA))

В зависимости от ваших требований у вас может быть параметр Map [T => Boolean, String] для строк метки, которые будут возвращаться: def find[T](l1 : List[T], fs : Map[T => Boolean, String]) или определять ваши собственные операторы.

Это оценит весь список, где поиск прерывается для первого найденного элемента.

1 голос
/ 01 февраля 2010

Вот вариант ответа Даниэля (и Ретронима).

Если вы просто хотите, чтобы предикат (из списка) был успешным, тогда вы можете использовать

def findP[T](list: Iterable[T], preds: Iterable[T=>Boolean]) = {
  list.view.map( x => (x , preds.find( _(x) )) ).find( _._2.isDefined )
}

Кроме того, вы можете использовать список именованных предикатов:

def findP[T](list: Iterable[T],preds: Iterable[(T=>Boolean,String)]) = {
  list.view.map(x => (x , preds.find( _._1(x) ))).find( _._2.isDefined )
}

scala> findP(
     |   List(1,2,3,4,5,6),
     |   List( ((i:Int)=>i>4,"Fred") , ((i:Int)=>(i%6)==0,"Barney"))
     | )
res2: Option[(Int, Option[((Int) => Boolean, String)])] =
  Some((5,Some((<function1>,Fred))))

Результат немного сумбурный, но его можно легко развернуть, чтобы получить именно то, о чем вы просили:

def findP[T](list: Iterable[T],preds: Iterable[(T=>Boolean,String)]) = {
  list.view.map(x => (x , preds.find( _._1(x) ))).find( _._2.isDefined ) match {
    case Some((i,Some((_,s)))) => Some((i,s))
    case _ => None
  }
}

(Это код для 2.8; переключите «просмотр» в «проекцию» для 2.7.)

...