2010-02-01 8 views
10

Ich habe das folgende Problem in scala. Ich muss das erste Element in einer Liste finden, das eine Prädikatfunktion mit zwei Bedingungen in OR erfüllt. Das Problem ist, dass ich das Element erhalten möchte, aber auch wissen möchte, welche der beiden Bedingungen erfüllt ist. Hier ist ein einfaches Beispiel:Suchen von Elementen in einer Skala-Liste und auch wissen, welches Prädikat erfüllt wurde

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

Ergebnis ist:

Some(B) 
Some(AA) 

Für den l1 Fall Ich mag würde einigen Rückgabewert habe (ein String zum Beispiel), die anzeigt, dass c1 erfüllt wurde (c2 für die l2 Fall). Eine mögliche Lösung könnte sein, eine Var vor dem Test zu definieren und sie innerhalb der c1 und c2 Funktionen zu setzen, aber ich würde gerne eine "funktionalere" Lösung finden, vielleicht etwas, das ein Tupel zurückgibt: (Element gefunden, Bedingung zufrieden).

Vielen Dank im Voraus für die Hilfe

Antwort

9

Ich würde dies tun:

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) 

Die view/projection stellt sicher, dass die Abbildung auf Abruf erfolgen wird, statt auf die ganze Liste angewendet werden.

+0

Verallgemeinert zu einer Liste von Prädikaten 'def findPredsOr [T] (l: Liste [T], ps: Liste [T => Boolesch]): Option [(T , List [Boolean])] = l.view.map (el => (el, ps.map (_. Apply (el)))) .find (t => t._2.contains (true)) ' – retronym

+0

Große Lösung. Danke für die Entdeckung/Ansicht, es scheint sehr nützlich zu sein! –

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

Je nach Bedarf können Sie einen Parameter Map haben [T => Boolean, String] für die Label-Strings zurück: def find[T](l1 : List[T], fs : Map[T => Boolean, String]) oder eigene Operatoren definieren.

Dies wird die ganze Liste auswerten, wo finds abbricht für das erste gefundene Element.

1

Hier ist eine Variante auf Daniels (und Retronyms) Antwort (en).

Wenn Sie nur das Prädikat wollen (aus einer Liste), die erfolgreich war, dann können Sie

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

Alternativ verwenden, können Sie eine Liste mit Namen Prädikate verwenden:

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

Das Ergebnis ein wenig überladen, aber kann leicht genug ausgepackt werden, um genau zu geben, was Sie verlangten:

(Dies ist Code für 2,8; switch "view" auf "projektion" für 2.7.)