首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在for-comprehension中使用List的组合选项会根据顺序导致类型不匹配

在for-comprehension中使用List的组合选项会根据顺序导致类型不匹配
EN

Stack Overflow用户
提问于 2011-01-18 09:09:53
回答 5查看 21.9K关注 0票数 83

为什么这种构造会在Scala中导致类型不匹配错误?

代码语言:javascript
复制
for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

<console>:6: error: type mismatch;
 found   : List[(Int, Int)]
 required: Option[?]
       for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

如果我将其中的一些与列表进行交换,它编译得很好:

代码语言:javascript
复制
for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))

这也可以很好地工作:

代码语言:javascript
复制
for (first <- Some(1); second <- Some(2)) yield (first,second)
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-01-18 09:38:26

For理解被转换为对mapflatMap方法的调用。例如这一条:

代码语言:javascript
复制
for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)

变成这样:

代码语言:javascript
复制
List(1).flatMap(x => List(1,2,3).map(y => (x,y)))

因此,第一个循环值(在本例中为List(1))将接收flatMap方法调用。由于List上的flatMap返回另一个List,因此for comprehension的结果当然是一个List。(这对我来说是新的:因为理解并不总是产生流,甚至不一定产生Seq。)

现在,让我们来看看flatMap是如何在Option中声明的

代码语言:javascript
复制
def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]

请记住这一点。让我们来看一下erroneous for converted (带有Some(1)的那个)是如何转换为一系列map调用的:

代码语言:javascript
复制
Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))

现在,很容易看出,flatMap调用的参数是返回List的参数,而不是所需的Option

为了修复这个问题,您可以执行以下操作:

代码语言:javascript
复制
for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)

这样编译就很好了。值得注意的是,正如人们通常认为的那样,Option不是Seq的子类型。

票数 119
EN

Stack Overflow用户

发布于 2011-01-18 13:23:57

记住一个容易记住的技巧,因为理解将尝试返回第一个生成器的集合的类型,在本例中为OptionInt。因此,如果你从一些(1)开始,你应该期望得到OptionT的结果。

如果你想要一个列表类型的结果,你应该从一个列表生成器开始。

为什么有这样的限制,而不是假设你总是想要某种序列呢?在某些情况下,返回Option是有意义的。也许你有一个Option[Int],你想结合一些东西来得到一个Option[List[Int]],比如用下面的函数:(i:Int) => if (i > 0) List.range(0, i) else None;然后你可以写这个,当事情不“有意义”时,你可以什么也没有得到:

代码语言:javascript
复制
val f = (i:Int) => if (i > 0) Some(List.range(0, i)) else None
for (i <- Some(5); j <- f(i)) yield j
// returns: Option[List[Int]] = Some(List(0, 1, 2, 3, 4))
for (i <- None; j <- f(i)) yield j
// returns: Option[List[Int]] = None
for (i <- Some(-3); j <- f(i)) yield j
// returns:  Option[List[Int]] = None

在一般情况下,如何扩展for理解实际上是一种相当通用的机制,可以将类型为M[T]的对象与函数(T) => M[U]相结合,以获得类型为M[U]的对象。在您的示例中,M可以是Option或List。通常,它必须是相同类型的M。所以你不能把Option和List结合起来。有关可以是M的其他东西的示例,请查看subclasses of this trait

但是,当你开始使用这个列表时,为什么将List[T](T) => Option[T]结合起来会起作用呢?在这种情况下,库在有意义的地方使用更通用的类型。所以你可以把List和Traversable结合起来,并且有一个从Option到Traversable的隐式转换。

底线是:考虑您希望表达式返回什么类型,并以该类型作为第一个生成器开始。如有必要,请将其包装在该类型中。

票数 32
EN

Stack Overflow用户

发布于 2011-01-18 09:41:53

这可能与Option不是Iterable有关。隐式Option.option2Iterable将处理编译器期望second为Iterable的情况。我期望根据循环变量的类型,编译器的魔力是不同的。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4719592

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档