簡體   English   中英

如何將F [A \\ / B]拆分為(F [A],F [B])

[英]How to split F[A \/ B] into (F[A], F[B])

我偶爾會遇到這樣的代碼:

val things : List[A \/ B] = ???
val (as, bs) : (List[A], List[B]) = ??? //insert something to do this

或者在我目前的情況下我想要Map[A, B \\/ C] => (Map[A, B], Map[A, C])

F[A \\/ B]的一般情況下是否有一個很好的方法來對F進行適當的限制? 它看起來有點像Unzip主題的變體。

以下是我們如何處理這個/但是也可以是Either和Validation,而不僅僅是列表,還有其他可折疊的。

object Uncozip {
  implicit val wtf = language.higherKinds

  // Typeclass which covers sum types such as \/, Either, Validation
  trait Sum2[F[_, _]] {
    def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: F[A, B]): X
  }

  implicit val sumEither: Sum2[Either] = new Sum2[Either] {
    def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: Either[A, B]): X = {
      fab match {
        case Left(l)  ⇒ a(l)
        case Right(r) ⇒ b(r)
      }
    }
  }

  implicit val sumEitherz: Sum2[\/] = new Sum2[\/] {
    def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: A \/ B): X = {
      fab.fold(a(_), b(_))
    }
  }

  implicit val sumValidation: Sum2[Validation] = new Sum2[Validation] {
    def cata[A, B, X](a: A ⇒ X, b: B ⇒ X)(fab: A Validation B): X = {
      fab.fold(a(_), b(_))
    }
  }

  abstract class Uncozips[F[_], G[_, _], A, B](fab: F[G[A, B]]) {
    def uncozip: (F[A], F[B])
  }

  implicit def uncozip[F[_]: Foldable, G[_, _], A, B](fab: F[G[A, B]])(implicit g: Sum2[G], mfa: ApplicativePlus[F], mfb: ApplicativePlus[F]): Uncozips[F, G, A, B] = new Uncozips[F, G, A, B](fab) {
    def uncozip = {
      implicitly[Foldable[F]].foldRight[G[A, B], (F[A], F[B])](fab, (mfa.empty, mfb.empty)) { (l, r) ⇒
        g.cata[A, B, (F[A], F[B])]({ (a: A) ⇒ (mfa.plus(mfa.point(a), r._1), r._2) },
          { (b: B) ⇒ (r._1, mfa.plus(mfa.point(b), r._2)) })(l)
      }
    }
  }
}

您可以將things映射到(Option[A], Option[B])列表中,將該列表解壓縮到兩個列表中,然后將結果列表聯合起來:

import scalaz._
import Scalaz._

val things: List[String \/ Int] = List("foo".left, 42.right)
val (strs, ints): (List[String], List[Int]) = things.
  map { d => (d.swap.toOption, d.toOption) }. // List[(Option[String], Option[Int])]
  unzip.                                      // (List[Option[String]], List[Option[Int]])
  bimap(_.unite, _.unite)                     // (List[String], List[Int])

由於遍歷列表三次,這不是特別有效。

這是一種方式(對於列表):

val things : List[A \/ B] = ???
val (as, bs) = (things.map(_.swap.toList).join, things.map(_.toList).join)

而對於地圖:

val things: Map[String, String \/ Int] = ???
val (as, bs) = (things.mapValues(_.swap.toList).filterNot(e => e._2.isEmpty), 
                things.mapValues(_.toList).filterNot(e => e._2.isEmpty))

我有一個很難拿出一個辦法在任何概括這個F (我相信你會需要的情況下, MonoidApplicativeF )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM