Поиск элементов в списке scala, а также знать, какой предикат был удовлетворен

у меня есть следующая проблема в 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 я хотел бы иметь некоторое возвращаемое значение (например, строку), указывающее, что C1 был удовлетворен (c2 для случая l2). Ля возможным решением может быть определение var перед тестом и установка его в функциях c1 и c2, но я хотел бы найти более "функциональное решение стиля", возможно, что-то, что возвращает кортеж, как: (элемент найден, условие выполнено).

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

3 ответов


Я бы сделал так:

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 гарантирует, что сопоставление будет выполняться по требованию, а не применяться ко всему списку.


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

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

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


вот такой вариант ответа Даниила (и Retronym) и(с).

Если вы просто хотите предикат (из списка), который преуспел, то вы можете использовать

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.)