【问题标题】:Scala Option and flatMapScala 选项和 flatMap
【发布时间】:2013-03-29 00:22:02
【问题描述】:

我试图掌握“Scala 方式”工作的窍门,所以我想知道下面的代码是否是在这种情况下应该如何完成的事情。

所以我有实体用户和公司(使用 LiftWeb 映射器映射)。用户的 currentUser 包含一个 Option[User],而 Company 的 currentCompany 是一个 Option[Company]。为了比较当前用户是否是当前公司的所有者,我正在做类似的事情:

Company.currentCompany.map{_.owner.get == User.currentUser.map{_.id.get}.openOr(-1) }.openOr(false)

它有效,但不知何故感觉有点冗长。好吗?不是吗?有更好的想法吗? 谢谢!

【问题讨论】:

  • 值得一提的是,currentUser 和 currentCompany 实际上是 Box 而不是 Option(Lift 对 Option 的扩展)。这就是我使用 openOr 而不是 getOrElse 的原因。然而,在这个简单的例子中,它是一样的。
  • 所以currentUser 返回一个Option[User]User.id 返回一个Option[Int]Company.owner 返回一个Option[Int]
  • @huynhjl 为了完全正确,User.currentUser 返回 Box[User] 并且 User.id 返回 MappedLongIndex 并继续返回 Long。 Company.currentCompany 通过返回 Box[Company] 和 Company.owner 返回 MappedLongForeignKey 并继续返回 Long。

标签: scala monads


【解决方案1】:

您是否考虑过使用for-comprehensions?您可以执行以下操作:

for(
  company <- Company.currentCompany.map{_.owner};
  user <- User.currentUser.map{_.id}
) yield (company == user).getOrElse(false)

如果 Company.currentCompany 是 Some[value],User.currentCompany 是 Some[value],并且 company.owner == user.id,这将返回 true。

我觉得应该有某种方法最终摆脱那个 getOrElse,并直接返回解包后的布尔值,希望其他人能够对此有所了解!

【讨论】:

  • 其实我觉得你可以把地图放到理解里。 for( 公司
  • 我认为你是对的。这看起来更清晰。它可能与我的代码编译成非常相似的东西。虽然你不能放弃 getOrElse ,但它应该是这样的。这是没有当前用户或当前公司时的默认设置。
  • .getOrElse(false) 是这里唯一明智的选择。我正要建议.isDefined,但这会丢失相等性检查的结果。当然,你不必马上getOrElse;在None 情况下进行模式匹配可能很有用,这在概念上与company != user 情况不同。
【解决方案2】:

使用 for-comprehension 绝对是解决方案,实际上...或flatMap 但可读性较差

回想一下,每个生成器都使用 Monadic OptionflatMap 函数绑定,除了最后一个被映射的(如任何 foryield)。这是一个很好的幻灯片,介绍了下面的概念Monad

因此,当所有步骤未编码为失败状态时,for 推导式用于通过所有步骤(None for Option)。

这是一个完整示例,每个选项(for 和 flatMap)都有四个测试(四个基本案例)

case class User(id: String) {

}


object User {

  def currentUser(implicit me: Option[User]): Option[User] = me

}

case class Company(owner: Option[User]) {

}


object Company {

  def currentCompany(implicit myCompany: Option[Company]): Option[Company] = myCompany

}

object Test extends App {

  test1()
  test2()
  test3()
  test4()
  test5()
  test6()
  test7()
  test8()

  def test1() {
    implicit val me: Option[User] = None
    implicit val myCompany: Option[Company] = None

    val v: Boolean = (for {
      c <- Company.currentCompany
      u <- User.currentUser
      o <- c.owner if o.id == u.id
    } yield true) getOrElse false

    println(v)
  }

  def test2() {
    implicit val me: Option[User] = Some(User("me"))
    implicit val myCompany: Option[Company] = None

    val v: Boolean = (for {
      c <- Company.currentCompany
      u <- User.currentUser
      o <- c.owner if o.id == u.id
    } yield true) getOrElse false

    println(v)
  }

  def test3() {
    implicit val me: Option[User] = None
    implicit val myCompany = Some(Company(me))

    val v: Boolean = (for {
      c <- Company.currentCompany
      u <- User.currentUser
      o <- c.owner if o.id == u.id
    } yield true) getOrElse false

    println(v)
  }

  def test4() {
    implicit val me: Option[User] = Some(User("me"))
    implicit val myCompany = Some(Company(me))

    val v: Boolean = (for {
      c <- Company.currentCompany
      u <- User.currentUser
      o <- c.owner if o.id == u.id
    } yield true) getOrElse false

    println(v)
  }

  def test5() {
    implicit val me: Option[User] = None
    implicit val myCompany: Option[Company] = None


    val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false

    println(v)
  }

  def test6() {
    implicit val me: Option[User] = Some(User("me"))
    implicit val myCompany: Option[Company] = None

    val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false

    println(v)
  }

  def test7() {
    implicit val me: Option[User] = None
    implicit val myCompany = Some(Company(me))

    val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false

    println(v)
  }

  def test8() {
    implicit val me: Option[User] = Some(User("me"))
    implicit val myCompany = Some(Company(me))

    val v:Boolean = Company.currentCompany.flatMap(c => User.currentUser.flatMap( u => c.owner.map(o => if (u.id == o.id) true else false))) getOrElse false

    println(v)
  }


}

【讨论】:

  • tl;dr: 因为理解只是flatMap 的糖;加糖调味。
  • 是的,这是我第二句话的一部分;)
  • 全面回复:) 谢谢!如果您将比较放在产量块中或像您一样直接放入,有什么区别吗?
  • 一点也不,这是一个选择的问题。我喜欢 for 中的它,因为我认为在 for 中包含所有测试和生成器会更清晰,并且只产生结果...这里是 true
【解决方案3】:

给定:

Case class user(name:String)
Case class company(owner:Option[User])

Val currentcompany=company(Some("Karl"))
Val currentuser=Some(user("Karl"))

可能的解决方案:

currentcompany.foldLeft(false) { 
  case (a,b) => currentuser.isDefined && b.owner == currentUser.get
}

【讨论】:

    猜你喜欢
    • 2015-07-22
    • 2017-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 2020-08-17
    • 2010-11-06
    • 1970-01-01
    相关资源
    最近更新 更多