2014-10-28 8 views
32

Jestem zagubiony przypadku użytkowania na temat biegu traverseU i traverseM, szukałem go na stronie internetowej scalaz, prosty przykład kodu:Jak zrozumieć biegu traverseU i traverseM

def sum(x: Int) = x + 1 

List(1,2,3).traverseU(sum) 

wygląda to podobna do (mapa i kruszywa):

List(1,2,3).map(sum).reduceLeft(_ + _) 

myślę, że jest bardziej niż dla traverseU, ja po prostu zastanawiam się, jaka jest różnica między tymi 3 metody, byłoby lepiej będę mieć jakiś przykładowy kod, aby pokazać różnica

Wiele z góry dzięki

Odpowiedz

60

sequence służy do zgromadzić efektów aplikacyjnych. Bardziej konkretnie, pozwala "przerzucić" F[G[A]] na G[F[A]], pod warunkiem, że G jest Applicative i F jest Traversable. Więc możemy użyć go do „współdziałać” pęczek Applicative efektów (uwaga wszystkich Monad s są Applicative):

List(Future.successful(1), Future.successful(2)).sequence : Future[List[Int]] 
// = Future.successful(List(1, 2)) 
List(4.set("abc"), 5.set("def")).sequence : Writer[String, List[Int]] 
// = List(4, 5).set("abcdef") 

traverse jest równoważna map następnie sequence, więc można go używać, gdy masz funkcję zwraca Applicative i chcesz po prostu jedno wystąpienie swoimi Applicative zamiast listy z nich:

def fetchPost(postId: Int): Future[String] 
//Fetch each post, but we only want an overall `Future`, not a `List[Future]` 
List(1, 2).traverse[Future, String](fetchPost): Future[List[String]] 

traverseU jest taka sama operacja jak traverse , tylko z typami wyrażonymi w inny sposób, aby kompilator mógł łatwiej ich wywnioskować.

def logConversion(s: String): Writer[Vector[String], Int] = 
    s.toInt.set(Vector(s"Converted $s")) 
List("4", "5").traverseU(logConversion): Writer[Vector[String], List[Int]] 
// = List("4", "5").map(logConversion).sequence 
// = List(4.set("Converted 4"), 5.set("Converted 5")).sequence 
// = List(4, 5).set(Vector("Converted 4", "Converted 5")) 

traverseM(f) jest równoważna traverse(f).map(_.join), gdzie join jest nazwą scalaz dla flatten. Jest to użyteczne jako swego rodzaju „podnoszenia flatMap”:

def multiples(i: Int): Future[List[Int]] = 
    Future.successful(List(i, i * 2, i * 3)) 
List(1, 10).map(multiples): List[Future[List[Int]]] //hard to work with 
List(1, 10).traverseM(multiples): Future[List[Int]] 
// = List(1, 10).traverse(multiples).map(_.flatten) 
// = List(1, 10).map(multiples).sequence.map(_.flatten) 
// = List(Future.successful(List(1, 2, 3)), Future.successful(List(10, 20, 30))) 
//  .sequence.map(_.flatten) 
// = Future.successful(List(List(1, 2, 3), List(10, 20, 30))).map(_.flatten) 
// = Future.successful(List(1, 2, 3, 10, 20, 30)) 
+0

Dzięki, upvote, więc po prostu zastanawiam się, wspomniałeś, że różnica między biegu i traverseU jest rodzaj wnioskowania, jest to jedyna różnica, bo o ile mi Jestem zaniepokojony, użyję tylko traverseU zamiast traverse –

+1

Tak, to jedyna różnica. (Technicznie używa dodatkowego parametru do wykonania wnioskowania, ale oczekiwałbym, że JVM to zoptymalizuje). W przypadku, gdy 'traverseU' * nie * wyprowadza parametrów typu i musisz je określić ręcznie (lub jeśli piszesz ogólny kod, w którym typ, przez który przechodzisz, jest parametrem typu), łatwiej jest Zrób to z 'traverse' (który nie ma pomocnika wnioskowania), ale myślę, że to jedyny przypadek, w którym kiedykolwiek chciałbyś użyć' trawersa 'zamiast' traverseU' – lmm

+0

BTW, myślę, że kod powinien być Future. teraz zamiast Future powiodło się, ponieważ poprzednia jest z scalaz, więc będzie miała niejawną wartość, ale późniejsza z biblioteki standardowej nie ma domyślnej rozdzielczości –