【问题标题】:How this stream's type is casted?这个流的类型是如何转换的?
【发布时间】:2016-04-23 06:43:32
【问题描述】:

我想知道下面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

【问题讨论】:

    标签: scala types casting type-conversion


    【解决方案1】:

    tl;博士:

    fibs 被定义为Stream[BigInt],因此当您在其前面添加Int (1 #:: ...) 时,编译器会查找从IntBigInt 的隐式转换并在@ 中找到它987654327@



    这里发生了几件事。

    1) BigInt 伴随对象定义了从IntBigInt隐式转换

    implicit def int2bigInt(i: Int): BigInt = apply(i)
    

    这意味着无论您需要BigInt,您都可以提供Int,隐式转换将转换该值。你也可以说Ints 可以被视为BigInts。

    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,但是,正如我之前提到的,Ints 可以被视为 BigInts,因此它们会自动转换为 BigInts 并且所有内容都会编译。


    当你这样做时:val s1:Stream[BigInt] = 1 #:: 2 #:: Stream.empty 您正在做不同的事情:您在右侧创建 Stream[Int]Stream.empty 类型被推断为 Int 从您传递的 1,2 值),然后您将此值分配给 @987654352 @瓦尔。

    不幸的是,没有从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
    

    Lists 还存在其他问题:与 Stream 不同,List 的缺点定义为:

    def ::[B >: A] (x: B): List[B]
    

    对于 Stream.#::(x),您需要 x 与您在 x 前面添加的 Stream 的类型完全相同。而使用List.::(x)x(类型为B)可以是列表类型的超类型 的实例。结果列表将是List[B],即添加到列表可以扩大其类型

    因此,当您执行2 :: List.empty[BigInt] 时,您实际上是在调用List[A].::(x: B),其中ABigInt,而B 推断为Any,因为Any 是最严格的超类型BigInt 也是 Int 的超类型

    由于这使编译器满意,因此不寻找隐式转换,结果列表是一个 List[Any],您不能再将其用作整数列表。

    您基本上可以调用whatever :: List[X] 来获得List[Y],其中YXwhatever 类型中最严格的超类型


    那么,为什么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)) 在申请 .::(1) 之前是 List[Any],所以我们无能为力

    • (List.empty[BigInt].::(BigInt(2))) 已经是 List[BigInt] 并且编译器可以尝试使 .::(1) 成为 BigInt(从而寻找隐式转换)

    【讨论】:

    • 你跳过了最有趣的案例val l1:List[BigInt] = 1 :: 2 :: List.empty[BigInt] 为什么,对于流,隐式转换开始了,但对于列表却没有,结果是List[Any]
    • @Łukasz 我花了一些时间来写答案,这个问题比我一开始想的要有趣得多。我可能在几件事上错了,但我认为或多或少就是这样!看看它,告诉我你的想法,随时编辑:)
    • 是的,我想现在你已经涵盖了所有内容。之前我从未注意到该流在追加时不会扩大类型,现在发生的事情很清楚:)
    • 我也没有!这就是为什么我认为这个问题很有趣:)
    • 对于s1,我想知道为什么它不能被推断,因为val s:Stream[BigInt] = Stream.empty 实际编译(不完全符合类型推断理论:-
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-29
    • 2014-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-11
    相关资源
    最近更新 更多