2010-09-04 11 views
12

Mam listę l:List[T1] i obecnie jestem w następujący sposób:scala powrót na pierwszą Niektórzy w liście

myfun : T1 -> Option[T2] 
val x: Option[T2] = l.map{ myfun(l) }.flatten.find(_=>true) 

Funkcja myfun zwróci None albo pół, spłaszczyć wyrzuca wszystkie Żaden oraz znaleźć Zwraca pierwszy element listę, jeśli istnieje.

Wydaje mi się to trochę hackowate. Im myślę, że może istnieć jakieś do zrozumienia lub podobne, które zrobi to nieco mniej marnotrawny lub bardziej sprytny. Na przykład: Nie potrzebuję żadnych kolejnych odpowiedzi, jeśli myfun zwraca dowolnąSome podczas map z listy .

Odpowiedz

12

Jak o:

l.toStream flatMap (myfun andThen (_.toList)) headOption 

Stream jest leniwy, więc to nie będzie mapować wszystko z góry, ale to nie będzie przemapować rzeczy albo. Zamiast spłaszczania rzeczy, przekonwertuj Option na List, aby można było użyć flatMap.

+1

Jeśli nie jestem zdezorientowany przeze mnie, "flatMap" może być również użyty w opcji, więc myślę "i wtedy (_.toList) "jest zbędny –

+0

@ Jen, jeśli go wypróbujesz, zobaczysz, że to nie działa. –

+1

Val L = Lista (1, 2, 3, 4, 5, 6) def zabawy (I int) = { gdy: (i == 3) Część (3) inny Brak } println (l .flatMap (fun (_)). head) println (l.flatMap (fun (_)). headOption) –

3

No to jest prawie, ale nie całkiem

val x = (l flatMap myfun).headOption 

Ale wracają do Option zamiast List od myfun, więc to może nie działać. Jeśli tak (nie mam rEPL pod ręką), a następnie spróbuj zamiast:

val x = (l flatMap(myfun(_).toList)).headOption 
2

Cóż, do-zrozumienia równoważne jest całkiem proste

(for(x<-l, y<-myfun(x)) yield y).headOption 

co jeśli faktycznie wykonać tłumaczenie odrabia to samo, co dawały oxbow_lakes. Zakładając uzasadnione lenistwo List.flatmap, jest to zarówno czyste, jak i wydajne rozwiązanie.

+2

Niestety, lista (np. collection.immutable.List) nie ma operacji leniwych. Z przyczyn, których nie rozumiem, zastąpienie 'l' przez' l.view' powoduje, że myfun jest wielokrotnie oceniany z tymi samymi argumentami. Widok –

+7

to nazwa po nazwie, nie leniwy. Jeśli chcesz dokonać oceny co najwyżej raz, użyj toStream: (l.toStream flatMap myfun) .headOption –

0

Od 2017 r. Poprzednie odpowiedzi wydają się nieaktualne. Przeprowadziłem testy porównawcze (lista 10 milionów int, pierwszy mecz mniej więcej pośrodku, Scala 2.12.3, Java 1.8.0, 1.8 GHz Intel Core i5).O ile nie zaznaczono inaczej, list i map mają następujące typy:

list: scala.collection.immutable.List 
map: A => Option[B] 

Wystarczy zadzwonić map na liście: ~ 1000 ms

list.map(map).find(_.isDefined).flatten 

najpierw wywołać toStream na liście: ~ 1200 ms

list.toStream.map(map).find(_.isDefined).flatten 

Zadzwoń na toStream.flatMap z listy: ~ 450 ms

list.toStream.flatMap(map(_).toList).headOption 

połączeń flatMap na liście: ~ 100ms

list.flatMap(map(_).toList).headOption 

najpierw wywołać iterator na liście: ~ 35 ms

list.iterator.map(map).find(_.isDefined).flatten 

funkcji rekurencyjnej find(): ~ 25 ms

def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = { 
    list match { 
    case Nil => None 
    case head::tail => map(head) match { 
     case None => find(tail, map) 
     case result @ Some(_) => result 
    } 
    } 
} 

Funkcja iteracyjna find(): ~ 25 ms

def find[A,B](list: scala.collection.immutable.List[A], map: A => Option[B]) : Option[B] = { 
    for (elem <- list) { 
    val result = map(elem) 
    if (result.isDefined) return result 
    } 
    return None 
} 

można dodatkowo przyspieszyć rzeczy za pomocą Java zamiast kolekcji Scala i styl mniej funkcjonalne.

pętli nad wskaźników w java.util.ArrayList: ~ 15 ms

def find[A,B](list: java.util.ArrayList[A], map: A => Option[B]) : Option[B] = { 
    var i = 0 
    while (i < list.size()) { 
    val result = map(list.get(i)) 
    if (result.isDefined) return result 
    i += 1 
    } 
    return None 
} 

pętli nad wskaźników w java.util.ArrayList z funkcją powrotu null zamiast None: ~ 10 ms

def find[A,B](list: java.util.ArrayList[A], map: A => B) : Option[B] = { 
    var i = 0 
    while (i < list.size()) { 
    val result = map(list.get(i)) 
    if (result != null) return Some(result) 
    i += 1 
    } 
    return None 
} 

(Oczywiście, zazwyczaj zadeklaruj typ parametru jako java.util.List, a nie java.util.ArrayList. Wybrałem ten ostatni tutaj, ponieważ jest to klasa, której użyłem do testów porównawczych.wyświetli inną wydajność - większość będzie gorsza.)

+0

Profilowanie JVM jest [notorycznie problematyczne] (https://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java). Z jakich narzędzi porównawczych korzystałeś? [Tymianek] (https://github.com/Ichoran/thyme)? JMH? Inny? – jwvh

+0

Wiem o problemach z testami Java. Nie użyłem żadnych narzędzi, tylko System.nanoTime(). Każda z tych liczb jest średnią z 100 uruchomień: uruchom JVM, wypełnij listę z 10 milionami losowych adresów, uruchom zegar, uruchom find() 100 razy, zatrzymaj zegar. Niezbyt precyzyjny, ale ponieważ istnieją różnice rzędu kilku rzędów wielkości, powiedziałbym, że ten prosty test porównawczy daje przynajmniej użyteczny przybliżony przegląd względnej skuteczności tych metod. –

+0

Podróżuję teraz, ale mam nadzieję, że będę miał czas na przesłanie (bardzo prostego) kodu, którego użyłem w ciągu najbliższych kilku dni. Chciałbym zobaczyć te same testy porównawcze, które zostały zmierzone za pomocą odpowiedniego narzędzia! –

Powiązane problemy