为什么这种构造会在Scala中导致类型不匹配错误?
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)如果我将其中的一些与列表进行交换,它编译得很好:
for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))这也可以很好地工作:
for (first <- Some(1); second <- Some(2)) yield (first,second)发布于 2011-01-18 09:38:26
For理解被转换为对map或flatMap方法的调用。例如这一条:
for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)变成这样:
List(1).flatMap(x => List(1,2,3).map(y => (x,y)))因此,第一个循环值(在本例中为List(1))将接收flatMap方法调用。由于List上的flatMap返回另一个List,因此for comprehension的结果当然是一个List。(这对我来说是新的:因为理解并不总是产生流,甚至不一定产生Seq。)
现在,让我们来看看flatMap是如何在Option中声明的
def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]请记住这一点。让我们来看一下erroneous for converted (带有Some(1)的那个)是如何转换为一系列map调用的:
Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))现在,很容易看出,flatMap调用的参数是返回List的参数,而不是所需的Option。
为了修复这个问题,您可以执行以下操作:
for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)这样编译就很好了。值得注意的是,正如人们通常认为的那样,Option不是Seq的子类型。
发布于 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;然后你可以写这个,当事情不“有意义”时,你可以什么也没有得到:
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的隐式转换。
底线是:考虑您希望表达式返回什么类型,并以该类型作为第一个生成器开始。如有必要,请将其包装在该类型中。
发布于 2011-01-18 09:41:53
这可能与Option不是Iterable有关。隐式Option.option2Iterable将处理编译器期望second为Iterable的情况。我期望根据循环变量的类型,编译器的魔力是不同的。
https://stackoverflow.com/questions/4719592
复制相似问题