用于理解可选的集合迭代和产量

For comprehension with optional collection iteration and yield

我不是很精通 Scala,并且真诚地发现文档很难尝试解决这个问题,但我想知道是否有人可以向我解释为什么以下语句的编译存在差异:

我可以轻松地遍历一组字符串并生成元素。

scala> for(name <- Set("asd", "123")) yield name
val res2: scala.collection.immutable.Set[String] = Set(asd, 123)

但是如果 Set 在 Option 中,我就不能内联了

scala> for(names <- Some(Set("asd", "123")); name <- names) yield (name)
                                              ^
   error: type mismatch;
    found   : scala.collection.immutable.Set[String]
    required: Option[?]

这是因为 for-yield 只是 flatMapmapwithFilter 函数的语法糖。所以,你的代码:

for(names <- Some(Set("asd", "123")); name <- names) yield (name)

实际上是一样的:

Some(Set("asd", "123")).flatMap{ names: Set[String] => 
  names.flatMap{ name: String => 
    name // return type is String, but Set[(of some )B] is expected
  }
}// return type is Set[String] but Option[(of some) C] is expected

查看OptionflatMap函数:

@inline final def flatMap[B](f: A => Option[B]): Option[B]

但是你的freturnsSet[String]

因此,编译器告诉您类型不匹配 (Set[String] != Option[?]):

error: type mismatch;
    found   : scala.collection.immutable.Set[String]
    required: Option[?]

你应该记住 for-yield 构造中第一个语句的类型。在您的情况下,它是 names <- Some(Set("asd", "123"))。它的类型为 Option[Set[String]],所以你应该只在 x <- yourNextStatement 行中使用 Option[T]x 应该有 Option[T] 类型)。

总结: 在 for-yield 结构中混合使用不同的容器类型时要小心。如果您遇到一些问题,请尝试将 for-yield 解包为 flatMapmapwithFilter 函数的组合。

如果你想在for-yeild中混合容器,你应该为每个-容器类型启动另一个for-yield .例如:

for { 
  names <- Some(Set("asd", "123"))
  // will return Option[String]
  reducedNames <- (for {
    name <- names // here will be used Seq flatMap function, not Option
  } yield (name + "some_suffix"))
    .reduceLeftOption(_ + _) // here we will get Option[String] from Seq[String]
} yield reducedNames // will return Option[String]

你希望 None 发生什么?假设“无”,你可以做

val optionalSet = Some(Set("asd", "123"))
for (names <- optionalSet.getOrElse(Set.empty); name <- names) yield name