我想知道下面的 Stream 的类型是如何转换的?
type NTy = BigInt
def streamFib(n: Int): NTy = {
lazy val fibs: Stream[NTy] = 1 #:: 1 #:: fibs.zip(fibs.tail).map(n => n._1 + n._2)
fibs.drop(n - 1).head
}
这编译和
streamFib
返回 BigInt
类型(如预期的那样?)。我还注意到其他一些行为:
val s1:Stream[BigInt] = 1 #:: 2 #:: Stream.empty // incorrect, found Int required BigInt
val s2:Stream[BigInt] = 1 #:: 2 #:: Stream.empty[BigInt] // correct
val l1:List[BigInt] = 1 :: 2 :: List.empty[BigInt] // incorrect, found Any required BigInt
val l2:List[BigInt] = 1 :: BigInt(2) :: List.empty[BigInt] // correct
最佳答案
tl;博士:fibs
被定义为 Stream[BigInt]
, 所以当你添加 Int
到它( 1 #:: ...
),编译器从 Int
寻找隐式转换至BigInt
并在 BigInt.int2bigInt
中找到它
这里发生了几件事。
1) BigInt
伴随对象定义了一个 隐式转换 来自 Int
至BigInt
:
implicit def int2bigInt(i: Int): BigInt = apply(i)
这意味着无论您在哪里需要
BigInt
您可以提供 Int
隐式转换将转换该值。你也可以说Int
s可以看成BigInt
s。2) 以冒号结尾的方法是右结合 .这意味着
1 #:: 2 #:: Stream.empty[BigInt]
可以有效地重写为 Stream.empty[BigInt].#::(2).#::(1)
现在,如果您查看
Stream.#::
的签名( def #::(hd: A): Stream[A]
) 你会看到 Stream[BigInt].#::(x)
只能编译 x
是 BigInt
.当您调用
1 #:: 2 #:: Stream.empty[BigInt]
您正在调用Stream[BigInt].#::
通过 Int
值而不是 BigInt
,但是,正如我之前提到的,Int
s可以看成BigInt
s,因此它们会自动转换为 BigInts 并且所有内容都会编译。当你这样做时:
val s1:Stream[BigInt] = 1 #:: 2 #:: Stream.empty
相反,您正在做不同的事情:您正在右侧创建 Stream[Int]
( Stream.empty
类型被推断为 Int 从您传递的 1,2 值)然后您将此值分配给 Stream[BigInt]
瓦尔。不幸的是,
Stream[A]
没有隐式转换。至Stream[B]
, 即使 A
可以看成B
,因此编译失败。您可以定义自己的隐式转换:
implicit def asBigIntStream(xs: Stream[Int]): Stream[BigInt] = xs.map(BigInt.int2bigInt)
val s1:Stream[BigInt] = 1 #:: 2 #:: Stream.empty //This now works
List
还有其他事情发生s:不同于Stream
, List
缺点定义为:def ::[B >: A] (x: B): List[B]
与
Stream.#::(x)
您需要 x 与您前面添加的 Stream 的类型完全相同 x
到。与 List.::(x)
,而是 x
(类型为 B
)可以是列表类型的父类(super class)型的实例。结果列表将是 List[B]
,即 添加到列表之前可以扩大其类型 .所以,当你做
2 :: List.empty[BigInt]
您实际上是在调用 List[A].::(x: B)
在哪里 A
是 BigInt
和 B
是 推断为 Any
因为Any
是 BigInt
的最严格父类(super class)型这也是 Int
的父类(super class)型 .由于这使编译器感到高兴,因此不会寻找隐式转换,并且结果列表是 List[Any] ,您不能再将其用作整数列表。
基本可以调用
whatever :: List[X]
获取 List[Y]
在哪里 Y
是两者中最严格的父类(super class)型 X
和 whatever
的类型那么,为什么不
val l1:List[BigInt] = 1 :: 2 :: List.empty[BigInt]
一边工作val l2 : List[BigInt] = 1 :: BigInt(2) :: List.empty[BigInt]
做? 这是因为类型推断。让我们重写删除右结合性的两个表达式:
val l1: List[BigInt] = (List.empty[BigInt].::(2)).::(1) // incorrect, found Any required BigInt
val l2: List[BigInt] = (List.empty[BigInt].::(BigInt(2))).::(1) // correct
我不是 100% 确定这一点(如果我错了,请任何人纠正我) :
::
的最后一个应用程序上帮助类型推断。(List.empty[BigInt].::(2))
是 List[Any]
在申请 .::(1)
之前所以我们无能为力(List.empty[BigInt].::(BigInt(2)))
已经是 List[BigInt]
并且编译器可以尝试使 .::(1)
一个 BigInt
(因此寻找隐式转换)关于scala - 这个流的类型是如何转换的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36807594/