2015-07-08 29 views
8

Załóżmy, że mam kilka funktorów zagnieżdżonych, np. List[Option[Int]] i trzeba zadzwonić pod numer map najbardziej wewnętrznego.Jak uprościć zagnieżdżone wywołania map?

Teraz używam zagnieżdżony maps:

scala> val opts: List[Option[Int]] = List(Some(0), Some(1)) 
opts: List[Option[Int]] = List(Some(0), Some(1)) 

scala> opts.map(o => o.map(_ + 1)) 
res0: List[Option[Int]] = List(Some(1), Some(2)) 

Co jeśli mam 3 poziomy zagnieżdżenia, na przykład?
Czy istnieje jakaś prosta alternatywa dla zagnieżdżonego maps?

Odpowiedz

7

Tak, jest to możliwe z scalaz.Functor:

scala> import scalaz.Functor 
import scalaz.Functor 

scala> import scalaz.std.list._ 
import scalaz.std.list._ 

scala> import scalaz.std.option._ 
import scalaz.std.option._ 

scala> Functor[List].compose[Option].map(List(some(0), some(1)))(_ + 1) 
res1: List[Option[Int]] = List(Some(1), Some(2)) 

Jest to jednak dłużej niż do po prostu zadzwoń pod numer map z zagnieżdżonym map. Jeśli często map struktur zagnieżdżonych, można tworzyć funkcje pomocnicze:

def map2[F[_], G[_], A, B](fg: F[G[A]])(f: A => B) 
    (implicit F0: Functor[F], G0: Functor[G]): F[G[B]] = 
    F0.map(fg)(g => G0.map(g)(f)) 

def map3[F[_], G[_], H[_], A, B](fg: F[G[H[A]]])(f: A => B) 
    (implicit F0: Functor[F], G0: Functor[G], H0: Functor[H]): F[G[H[B]]] = 
    F0.map(fg)(g => G0.map(g)(h => H0.map(h)(f))) 

... 

Zastosowanie:

scala> map2(List(some(0), some(1)))(_ + 1) 
res3: List[Option[Int]] = List(Some(1), Some(2)) 

scala> map3(List(some(some(0)), some(some(1))))(_ + 1) 
res4: List[Option[Option[Int]]] = List(Some(Some(1)), Some(Some(2))) 
0

Od pytania zrozumiałem, że wiążesz się z przycinaniem listy iterator e.i. usuń górne poziomy listy, w takim przypadku możesz użyć flatten, która zamienia listę list w jedną listę.

będę usunięcie kilku warstw listy używając flatten

Code: -

val lists = List( 
        List( 
         List(
           List("1"),List("2") 
          ), 
         List(
           List("3"),List("4") 
          ) , 
         List(
           List("a"),List("b") 
          ), 
         List(
           List("c"),List("d") 
          ) 
          ) 
       ) 

val innerVal = lists.flatten.foreach(println) 

Wyniki: -

List(List(1), List(2)) 
List(List(3), List(4)) 
List(List(a), List(b)) 
List(List(c), List(d)) 
+0

nie jestem zakładając, że moje funktory są monady więc nie można użyć 'flatten'. – Michael

5

Jeśli masz dużo zagnieżdżonych funktorów i don” chcesz je spłaszczyć (tj. nie są monadami lub nie chcesz ich używać jako monad) - wtedy obiektywy mogą pomóc. Istnieje implementacja quicklens, która obsługuje przesuwne soczewki: http://www.warski.org/blog/2015/03/quicklens-traversing-options-and-lists/.

Przykład (przepraszam nie próbował go skompilować):

modify(opts)(_.each.each).using(_ + 1) 

W każdym razie, trzeba określić poziom zagnieżdżenia, ale nie masz do funkcji gniazdo tutaj. I to wystarczy, aby określić go raz, jak (koncepcyjnej przykład, nie sprawdzałem):

def md2[T]: (l: List[Option[T]]) => modify(l)(_.each.each) 

md2[Int](opts).using(_ + 1)