scala中令人难以置信的隐式数组转换

时间:2011-11-27 09:28:43

标签: arrays scala implicit

根据Scaladoc,map类中没有名为Array的方法,但implicit def intArrayOps (xs: Array[Int]): ArrayOps[Int]中定义了隐式函数scala.Predef。因此,如果您愿意,可以在map上应用Array(1,2,3,4)。但我感到困惑的是,map结果的类型为Array[Int],而不是ArrayOps[Int]。这是我的测试:

scala> val array = Array(1,2,3,4)
array: Array[Int] = Array(1, 2, 3, 4)

scala> array.map(x => x)
res18: Array[Int] = Array(1, 2, 3, 4)

scala> res18.isInstanceOf[Array[Int]]
res19: Boolean = true

scala> res18.isInstanceOf[scala.collection.mutable.ArrayOps[Int]]
warning: there wre 1 unchecked warnings; re-run with -unchecked for details
res20: Boolean = false

2 个答案:

答案 0 :(得分:11)

它确实返回了一个数组,按照预期方便,没有理由需要一个ArrayOps,它只是为数组提供额外的方法。该文档是错误的。

该例程实际上未在ArrayOps中实现。作为大多数收集方法,它继承自TraversableLike。你在doc中看到了两种map方法:

def map [B] (f: (T) ⇒ B): ArrayOps[B]
def map [B, That] (f: (T) ⇒ B)(implicit bf: CanBuildFrom[Array[T], B, That]): That

仅存在第二个(继承自TraversableLike)。它旨在允许在一个地方(可遍历的地方)实施地图,同时总是提供最好的行为。例如,String是Seq [Char]的一个,如果你用一个函数从一个字符映射到另一个字符,你得到一个String,但是如果你从集合映射到Int,结果不能是一个字符串,它只是一个Seq。文章fighting the bit rot with types中详细解释了这一点。

但是,这会产生一个非常复杂的签名,这不会反映使用该方法的简单性,并且在大多数情况下都会生成非常差的文档(通常您必须追逐隐式作用域中的CanBuildFrom才能工作)。这在this most famous scala question of stack overflow中讨论过。因此扩展了工具scaladoc,以便可以出现与预期用法相对应的更简单的条目。如果您查看引入例程的GenTraversableLike source,您将在scaladoc for map中看到以下内容(以及许多方法中的类似内容)

@usecase def map[B](f: A => B): $Coll[B]

子类型添加在他们的文档@define Coll <className>中,并且地图(以及其他)显示简化的签名,标记为[用例]。在ArrayOps的{​​{3}}中,@define Coll ArrayOps应该是Array

答案 1 :(得分:2)

您可以将REPL与-Xprint:typer选项一起使用,以查看正在发生的事情。以下是map方法的输出,重新格式化以便于阅读:

$ scala -Xprint:typer

scala> Array(1,2,3,4).map(x => x)
[[syntax trees at end of typer]]// Scala source: <console>
// some lines deleted
private[this] val res0: Array[Int] =
  scala.this.Predef.intArrayOps(scala.Array.apply(1, 2, 3, 4))
   .map[Int, Array[Int]]
     (( (x: Int) => x ))
     (scala.this.Array.canBuildFrom[Int](reflect.this.Manifest.Int));

这样简化包名称就是这样:

intArrayOps(Array(1,2,3,4)) // converts to ArrayOps
  .map[Int, Array[Int]]     // calls map with parameter lists below
    ((x:Int) => x)          // pass identity function as fisrt param
    (Array.canBuildFrom[Int]// pass builder for Array[Int] as second param
      (Manifest.Int))       // pass class manifest for Int
  1. 所以确实转换为ArrayOps(第一行)。它返回ArrayOps[Int]
  2. 然后调用ArrayOps.map[Int, Array[Int]]方法。然后就像didierd解释的那样,map的原始签名 - 而不是简化签名 - 表明推断的返回类型将是Array[Int]