2015-07-08 14 views
8

Giả sử tôi có một vài functors lồng nhau, ví dụ: List[Option[Int]] và cần gọi số map của hình bên trong nhất.Làm thế nào để đơn giản hóa các cuộc gọi bản đồ lồng nhau?

Bây giờ tôi đang sử dụng lồng nhau 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)) 

gì nếu tôi có 3 cấp độ làm tổ, ví dụ?
Có cách thay thế đơn giản nào để lồng vào nhau maps không?

Trả lời

7

Có, đây là có thể với 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)) 

Tuy nhiên, đây là lâu hơn để chỉ cần gọi map với số lồng map. Nếu bạn thường xuyên lập bản đồ cấu trúc lồng nhau, bạn có thể tạo các hàm helper:

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

... 

Cách sử dụng:

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

Từ câu hỏi tôi hiểu rằng bạn đang buộc phải cắt bớt danh sách vòng lặp e.i. loại bỏ các cấp trên của danh sách trong trường hợp đó bạn có thể sử dụng flatten chuyển đổi danh sách các danh sách thành một danh sách duy nhất.

tôi sẽ được loại bỏ vài lớp danh sách sử dụng 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) 

kết quả: -

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

Tôi không giả định rằng functors của tôi là monads vì vậy không thể sử dụng 'flatten'. – Michael

5

Nếu bạn có rất nhiều functors lồng nhau và bạn don' t muốn san bằng chúng (nghĩa là chúng không phải là các monads hoặc bạn không muốn sử dụng chúng như monads) - sau đó các ống kính có thể hữu ích. Có triển khai thực hiện quicklens, hỗ trợ các ống kính đi ngang: http://www.warski.org/blog/2015/03/quicklens-traversing-options-and-lists/.

Ví dụ (xin lỗi đã không cố gắng để biên dịch nó):

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

Dù sao, bạn phải xác định mức độ làm tổ, nhưng bạn không có chức năng tổ ở đây. Và đó là đủ để xác định nó một lần, tương tự (ví dụ khái niệm, không kiểm tra):

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

md2[Int](opts).using(_ + 1)  
Các vấn đề liên quan