首页 文章

如何编写在Scala中返回Option [List]的函数?

提问于
浏览
3

假设我有两个函数来获取订单和订单商品:

def getOrders(): Option[List[Int]] = ...
def getOrderItems(orderId: Int): Option[List[Int]] = ...

请注意,由于每个函数都可能失败,因此两个函数都返回 Option[List] .

现在我想获得所有订单商品的 Option ,如下所示:

如果两个函数都返回 Some

  • 返回 Some[List]

  • None 如果其中任何一个返回None .

我尝试用 for (见下文)组合这些函数,但它没有用 .

val allOrderItems = for {
  orderIds   <- getOrders();
  orderId    <- orderIds;
  orderItems <- getOrderItems(orderId)
} yield orderItems

如何使用函数 getOrdersgetOrderItems 构建函数 getAllOrderItems():Option[List[Int]]

2 回答

  • 2

    你真的希望能够将 Option[List[Option[List[Int]]]] 的中间两层内部翻出来,这样你就可以获得彼此相邻的选项和列表 . 此操作称为排序,由Scalaz提供:

    import scalaz._, Scalaz._
    
    val items: Option[List[Int]] =
      getOrders.flatMap(_.map(getOrderItems).sequence).map(_.flatten)
    

    你可以等效地使用 traverse ,它结合了 mapsequence 操作:

    val items: Option[List[Int]] =
      getOrders.flatMap(_ traverse getOrderItems).map(_.flatten)
    

    如果您不想使用Scalaz,您可以编写自己的(更少多态) sequence

    def sequence[A](xs: List[Option[A]]) = xs.foldRight(Some(Nil): Option[List[A]]) {
      case (Some(h), Some(t)) => Some(h :: t)
      case _ => None
    }
    

    然后:

    val items: Option[List[Int]] = getOrders.flatMap(
      orderIds => sequence(orderIds.map(getOrderItems))
    ).map(_.flatten)
    

    monad转换解决方案实际上非常简单(如果您愿意使用Scalaz):

    val items: Option[List[Int]] = (
      for {
        orderId <- ListT(getOrders)
        itemId  <- ListT(getOrderItems(orderId))
      } yield itemId
    ).underlying
    

    这种方法的好处是你不必考虑你需要展平,顺序等等 - 普通的monadic操作完全符合你的要求 .

  • 6

    我能想到的最简单的修改如下:

    for{
        orderId <- getOrders.getOrElse(Nil)
        items <- getOrderItems(orderId)
    } yield items
    

    for comprehension使用第一个语句来确定其余的类型 . 例如,在上面的类型 List[Int] 将被推测,这与 Option[List[Int]] 不同 .

相关问题