2

诚然,我对 Scala 很陌生,而且我在使用许多 Scala 示例中看到的语法糖时遇到了麻烦。它通常会产生一个非常简洁的陈述,但老实说到目前为止(对我来说)有点难以理解。

因此,我希望将 Option 类的典型用法(安全解引用)作为理解的一个很好的起点,例如,在我见过的特定示例中使用下划线。

我发现了一篇非常好的文章,展示了使用 Option 来避免 null 情况的示例。

https://medium.com/@sinisalouc/demystifying-the-monad-in-scala-cc716bb6f534#.fhrljf7nl

他这样描述一个用途:

trait User {
  val child: Option[User]
}

顺便说一句,您也可以将这些函数编写为就地 lambda 函数,而不是先验地定义它们。然后代码变成了这样:

val result = UserService.loadUser("mike")
  .flatMap(user => user.child)
  .flatMap(user => user.child)

看起来很棒!也许不像 groovy 那样简洁,但还不错。

所以我想我会尝试将它应用于我试图解决的案例。

我有一个类型Person,其中 a 的存在Person是可选的,但如果我们有一个人,他的属性是有保证的。因此,OptionPerson类型本身中没有使用类型。

Person有一个类型为 的PID Id。该Id类型由两种String类型组成;Id-Type 和 Id-Value。

我使用 Scala 控制台测试了以下内容:

class Id(val idCode : String, val idVal : String)

class Person(val pid : Id, val name : String)

val anId: Id = new Id("Passport_number", "12345")

val person: Person = new Person(anId, "Sean")

val operson : Option[Person] = Some(person)

好的。这设置了我的人,它是可选的实例。

我从上面链接的文章中了解到,我可以使用 flatMap 获取 Persons Id-Val;像这样:

val result = operson.flatMap(person => Some(person.pid)).flatMap(pid => Some(pid.idVal)).getOrElse("NoValue")

伟大的!这样可行。如果我实际上没有人,我的结果是“NoValue”。

我使用了 flatMap(而不是 Map),因为除非我误解(并且我对 Map 的测试不正确),否则如果我使用 Map,我必须提供备用或默认 Person 实例。我不想做的。

好的,所以,flatMap 是要走的路。

但是,这确实不是一个非常简洁的陈述。如果我用一种更时髦的风格来写它,我想我可以做这样的事情:

val result = person?.pid.idVal

哇,这有点好看!

Scala 肯定有办法提供至少与 Groovy 一样好的东西吗?

在上面的链接示例中,他能够使用我之前提到的一些语法糖使他的陈述更加简洁。下划线:

甚至更简洁:

val result = UserService.loadUser("mike")
  .flatMap(_.child)
  .flatMap(_.child)

因此,在这种情况下,下划线字符似乎允许您跳过指定类型(因为类型是推断出来的)并将其替换为下划线。

但是,当我用我的例子尝试同样的事情时:

val result = operson.flatMap(Some(_.pid)).flatMap(Some(_.idVal)).getOrElse("NoValue")

斯卡拉抱怨。

<console>:15: error: missing parameter type for expanded function ((x$2) => x$2.idVal)
       val result = operson.flatMap(Some(_.pid)).flatMap(Some(_.idVal)).getOrElse("NoValue")

有人可以在这里帮助我吗?

我怎么会误解这个?有没有写我上面冗长的陈述的速记方法?flatMap 是实现我所追求的最佳方式吗?还是有更好的更简洁和/或可读的方式来做到这一点?

提前致谢!

4

2 回答 2

9

你为什么坚持使用flatMap?我只是用map你的例子代替:

val result = operson.map(_.pid).map(_.idVal).getOrElse("NoValue")

甚至更短:

val result = operson.map(_.pid.idVal).getOrElse("NoValue")

您应该只使用返回s 的flatMap函数。Option你的pididVals 不是Options,所以只需映射它们。

你说

我有一个 Person 类型,其中 Person 的存在是可选的,但如果我们有一个人,他的属性是有保证的。因此,在 Person 类型本身中没有使用 Option 类型。

这是您的示例与示例之间的本质区别User。在User示例中,实例的存在User及其child字段都是选项。这就是为什么,要获得child,您需要flatMap。但是,由于在您的示例中,Person不能保证 a 的存在,因此在您检索 a 之后Option[Person],您可以安全地映射到它的任何字段。

flatMap其视为 a map,后跟 a flatten(因此得名)。如果我映射到孩子:

val ouser = Some(new User())
val child: Option[Option[User]] = ouser.map(_.child)

我最终会得到一个Option[Option[User]]. 我需要将其展平为一个Option级别,这就是我flatMap首先使用的原因。

于 2016-02-08T09:58:50.490 回答
7

如果您正在寻找最简洁的解决方案,请考虑以下事项:

val result = operson.fold("NoValue")(_.pid.idVal)

尽管人们可能会发现它不清楚或令人困惑

于 2016-02-08T10:05:08.840 回答