【问题标题】:Scala: strange type mismatch errorScala:奇怪的类型不匹配错误
【发布时间】:2010-07-25 08:21:29
【问题描述】:

假设我有一个这样的函数(这只是一个例子,所以不要为我提供更好的方法来创建 0,1,2,... 样式数组):

def createArray(size: Int): Array[Int] = {
  for (i <- 0 until size) yield i
}

但是编译器对一些神秘的类型不匹配错误感到不安:

(fragment of compare-images.scala):39: error: type mismatch;
 found   : RandomAccessSeq.Projection[Int]
 required: Array[Int]
      for (i <- 0 until size) yield i
            ^
one error found
!!!
discarding <script preamble>

我敢肯定,原因与until 方法的返回类型是Range,而不是Array 这一事实有关。然而,为什么编译器不能只转换类型? 有趣的是,当我删除函数的返回类型时,问题就消失了:

def createArray(size: Int) = {
  for (i <- 0 until size) yield i
}

但我希望我的函数返回 Array...

我还有另一个函数,是这样的:

def screateArray2: Array[Int] = {
  val a = Array(1,2,3,4,5)
  for (i <- a) yield i
}

它编译没有任何问题。它产生的值与第一个示例非常相似,但不使用 until 方法...

我是否遗漏了有关 Scala 类型系统的某些内容?

我对 Scala 还是很陌生。

编辑:我有点像这样解决了我的问题:

def crop(data: Array[Int]): Array[Int] = (
  for (i <- 0 until data.size) yield i
).toArray

但在我看来,它一点也不可读……

【问题讨论】:

    标签: arrays scala range


    【解决方案1】:

    您不能仅仅因为 Array 不是 Range,也不是它的超类而强制转换类型。我认为他们最常见的超类型是IndexedSeq。因此,您的示例类似于您声明返回 Int 的方法,而实现将返回 String,例如。 Range 确实有一个 toArray 方法,但是,如果你想使用 until 样式并返回一个数组,你可以这样做:

    
    scala> (for (i <- 0 until 5) yield i).toArray
    res0: Array[Int] = Array(0, 1, 2, 3, 4)
    

    或者,如果不喜欢这样,还有另一种方法,但不使用 until 样式:

    
    scala> for (i <- Array.range(0,5)) yield i          
    res1: Array[Int] = Array(0, 1, 2, 3, 4)
    

    另外,检查 this related question 是否从 for 理解返回自定义类型

    【讨论】:

    • 我应该说“推断”(或任何 Scala 编译器为匹配类型所做的)而不是“强制转换”。
    • 但是类型不匹配,因为 Range 不是 Array 的子类型,所以这里不存在推断正确结果类型的问题。您必须进行转换才能获得它。
    • 是的,但您真的不必是天才编译器即可自动将 Range 转换为 Array,而不是因类型不匹配错误而困扰我 :)
    • 我认为您必须是编译器天才才能在任意类型之间自动强制转换。
    • @Vilius 请注意,编译器不会打扰您。意思是您可能编写了错误的代码,因为您正在生成一种类型的数据并返回另一种类型。
    【解决方案2】:

    那么,这个怎么样:

    scala> import scala.collection.breakOut
    import scala.collection.breakOut
    
    scala> def createSet(size: Int): Set[Int] = (
         |     for(i <- 0 until size) yield i
         | )(breakOut)
    createSet: (size: Int)Set[Int]
    
    scala> def createList(size: Int): List[Int] = (
         |     for(i <- 0 until size) yield i
         | )(breakOut)
    createList: (size: Int)List[Int]
    
    scala> def createArray(size: Int): Array[Int] = (
         |     for(i <- 0 until size) yield i
         | )(breakOut)
    createArray: (size: Int)Array[Int]
    
    scala> createArray(10)
    res3: Array[Int] = Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    
    scala> createList(5)
    res4: List[Int] = List(0, 1, 2, 3, 4)
    
    scala> createSet(4)
    res5: Set[Int] = Set(0, 1, 2, 3)
    
    scala>
    

    【讨论】:

    • (breakOut) 部分到底是做什么的?我抱怨我的.toArray 解决方案不可读:)
    • @Vilius 是的,这不是特别可读,我什至不认为这是可能的。查找有关 Scala 的 for-comprehension 和 breakOut 的问题,这在别处有解释。
    【解决方案3】:

    Arjan 的解决方案可以简化为:

     def createArray(size: Int) = (0 until size).toArray
    

    [编辑] 当然你可以在创建数组之前删除不需要的值

     def createArray(size: Int) = (0 until size).filter(LOTSOFINTERESTINGSTUFF).toArray
    

    【讨论】:

    • 我的实际方法更像for (i &lt;- 0 until size; if (LOTS OF STUFF HERE)) yield i。我只是为了举例而简化了它。
    • 其实这里withFilter会比filter好。
    猜你喜欢
    • 2014-09-13
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多