在Scala上输入不匹配以进行理解
-
12-10-2019 - |
题
为什么这种结构会导致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)
解决方案
为了理解,将其转换为呼叫 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
方法调用。自从 flatMap
在 List
返回另一个 List
, ,理解的结果当然是 List
. 。 (这对我来说是新的:因为综合并不总是会导致流,甚至不一定在 Seq
s。)
现在,看看如何 flatMap
被声明 Option
:
def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]
请记住这一点。让我们看看如何错误地理解 Some(1)
)转换为一系列地图调用:
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
, ,正如通常假定的那样。
其他提示
一个容易记住的提示, 用于理解 在这种情况下,将尝试返回第一个生成器的集合的类型,选项[int]。因此,如果您从 一些(1) 您应该期望选择[T]的结果。
如果您想要的结果 列表 类型,您应该从列表生成器开始。
为什么要受到这种限制,而不是假设您始终需要某种序列?您可以有一个有意义的返回的情况 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
如何 用于理解 实际上,在一般情况下扩展是一种相当普遍的机制,可以结合一种类型的对象 M[T]
使用功能 (T) => M[U]
获取类型的对象 M[U]
. 。在您的示例中,M可以是选项或列表。通常,它必须是相同的类型 M
. 。因此,您无法将选项与列表结合在一起。对于其他可能是其他事物的例子 M
, , 看着 这个特征的子类.
为什么组合 List[T]
和 (T) => Option[T]
虽然从列表开始时工作?在这种情况下,库使用更有意义的更通用类型。因此,您可以将列表与可穿越的列表相结合,并且有一个隐含的转换从选项到可遍历。
最重要的是:考虑您希望表达式返回的类型,并以该类型作为第一个生成器开始。如有必要,将其包裹在该类型中。
它可能与不觉得的选项有关。隐式 Option.option2Iterable
将处理编译器期望第二名的情况。我希望根据循环变量的类型,编译器魔法有所不同。
我总是发现这很有帮助:
scala> val foo: Option[Seq[Int]] = Some(Seq(1, 2, 3, 4, 5))
foo: Option[Seq[Int]] = Some(List(1, 2, 3, 4, 5))
scala> foo.flatten
<console>:13: error: Cannot prove that Seq[Int] <:< Option[B].
foo.flatten
^
scala> val bar: Seq[Seq[Int]] = Seq(Seq(1, 2, 3, 4, 5))
bar: Seq[Seq[Int]] = List(List(1, 2, 3, 4, 5))
scala> bar.flatten
res1: Seq[Int] = List(1, 2, 3, 4, 5)
scala> foo.toSeq.flatten
res2: Seq[Int] = List(1, 2, 3, 4, 5)