scala - 这个流的类型是如何转换的?

标签 scala types casting type-conversion

我想知道下面的 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伴随对象定义了一个 隐式转换 来自 IntBigInt :

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)只能编译 xBigInt .

当您调用 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)在哪里 ABigIntB推断为 Any因为AnyBigInt 的最严格父类(super class)型这也是 Int 的父类(super class)型 .

由于这使编译器感到高兴,因此不会寻找隐式转换,并且结果列表是 List[Any] ,您不能再将其用作整数列表。

基本可以调用whatever :: List[X]获取 List[Y]在哪里 Y是两者中最严格的父类(super class)型 Xwhatever 的类型

那么,为什么不 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/

    相关文章:

    c++ - c++ 枚举的底层类型是什么?

    casting - 如何在数字类型之间进行转换?

    java - 无法从 Set<Map.Entry<String,capture#2-of 转换?将 A>> 扩展为 Set<Map.Entry<String,?延伸A>>

    scala - 合成两张 map

    java - 如何从可变 Java 映射构建 Scala 不可变映射?

    java - 检查 javax.lang.model.type.TypeMirror 是否为原始类型

    c++ - 将 PyArrayObject* 转换为 int* 失败

    java - 无法识别客户端 http header 何时完成

    Scala - 获取绑定(bind)变量列表?

    java - 使用 NOT 运算符时的字节转换