2015-07-08 13 views
8

Supponiamo di avere alcuni funtori annidati, ad es. List[Option[Int]] e devi chiamare lo map del più interno.Come semplificare le chiamate di mappe annidate?

Ora sto utilizzando nidificato 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)) 

Che cosa succede se ho 3 livelli di nidificazione, per esempio?
Esiste una semplice alternativa al nidificato maps?

risposta

7

Sì, questo è possibile con 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)) 

Tuttavia, questo è più lungo di a chiama semplicemente map con uno nidificato map. Se spesso mappare strutture nidificate, è possibile creare funzioni di aiuto:

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

... 

Usage:

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

Dalla domanda ho capito che stai legando per potare l'elenco iteratore e.i. rimuovere i livelli superiori dell'elenco in questo caso è possibile utilizzare flatten per convertire l'elenco di elenchi in un unico elenco.

sarò rimuovendo alcuni strati di elenco utilizzando flatten

Codice: -

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) 

risultati: -

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

io non parto dal presupposto che i miei funtori sono monadi in modo da non possono utilizzare 'flatten'. – Michael

5

Se si dispone di un sacco di funtori nidificate e si don' t voglio appiattirli (cioè non sono monadi o non li vuoi usare come monadi) - allora le lenti possono aiutarti. Esiste un'implementazione quicklens, che supporta obiettivi traversabili: http://www.warski.org/blog/2015/03/quicklens-traversing-options-and-lists/.

Esempio (mi dispiace non provare a compilarla):

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

In ogni caso, è necessario specificare il livello di nidificazione, ma non c'è bisogno di funzioni nido qui. Ed è sufficiente specificare una volta, come (ad esempio concettuale, non ha controllato):

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

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