scala - 为什么 For Comprehension 生成器会抑制 Option 类型?

在下面的代码中,版本 1 给出了正确的结果。我对 V2 做了一个小改动。 None 值消失了,这很好,因为 For Expression 就是这样工作的。但是,V2 中的 yield 输出不再遵循 myList.lift() 返回的数据类型(如在 V1 中一样)的原因是什么?

val myList = List(12, 34, "ABC")

版本 1

for { i <- (0 to 3).toList } yield myList.lift(i)
// res1: List[Option[Any]] = List(Some(12), Some(34), Some(ABC), None)

版本 2

for { 
  i <- (0 to 3).toList 
  x <- myList.lift(i)
} yield x
// res2: List[Any] = List(12, 34, ABC)

最佳答案

第一种情况的脱糖:

// desugar for comprehension:
(0 to 3).toList.map(
  i => myList.lift(i))

对第二种情况进行脱糖:

// desugar for comprehension:
(0 to 3).toList.flatMap(
  i => myList.lift(i).map(
    x => x))

// remove .map(x => x):
(0 to 3).toList.flatMap(
  i => myList.lift(i))

// desugar flatMap:
(0 to 3).toList.map(
  i => myList.lift(i)).flatten

第二种情况简化为第一种情况,末尾有 .flatten,这解释了结果的差异:res2 = res1.flatten

到底发生了什么?

Scala 可以将 Option 视为一个序列:

Some(foo) --> Seq(foo)
None      --> Seq()

.flatten 只是将序列序列展平。

如果你对类型感到好奇:

  • scala.collection.Seq.flatten 要求“内部”类型隐式转换为 GenTraversableOnce[T]
  • 有一个从 Option[T]Iterable[T] 的全局隐式转换
  • Iterable[T] <: GenTraversableOnce[T]

<- 中的 x <- myList.lift(i) 不只是将变量分配给一个值,它“从”myList.lift(i) 中“获取一个值”。当您从 Option[T] 中“获取一个值”时,您将获得 fooSome(foo)None 没有任何内容“一无所获”意味着 yield 根本不运行 None ,因此当 i = 3 时,“迭代”的结果中没有显示任何内容。

如果您对这个为 SeqOption 和 Scala 中的许多其他类型定义的“获取值”概念感到好奇,那么它是为任何 Monad 定义的。

https://stackoverflow.com/questions/38820577/

相关文章:

python - 如何通过 Django 查看上传的文本文件?

string - 如何在 go 模板中连接两个字符串?

oracle - 更新分区键,不允许行移动

php - 使用 cronjob 运行 php 文件中的函数

c# - 如何在装饰器中定义事件和属性

php - 如何过滤此 PHP 数组对象中的唯一对象

java - 将 List> 转换为 int[][]

laravel - 为什么在 Laravel 中注销路由不起作用?

php - 上传文件在php中不起作用

django - 不可散列类型 : 'list' error when trying to add