невероятное неявное преобразование массива в Scala - PullRequest
7 голосов
/ 27 ноября 2011

Согласно Scaladoc, в классе Array нет метода с именем map, но в scala.Predef определена неявная функция implicit def intArrayOps (xs: Array[Int]): ArrayOps[Int]. Так что вы можете применить 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 ]

11 голосов
/ 27 ноября 2011

Он действительно возвращает массив, как и предполагалось, и, как это удобно, нет причин, по которым вам понадобится ArrayOps, он предназначен только для предоставления дополнительных методов для массивов.Док не прав.

Процедура фактически не реализована в ArrayOps.Как и большинство методов сбора, он унаследован от TraversableLike.И в документе вы видите два метода карты:

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, результат не может быть String, и он будет простосекв.Это объяснено более подробно в статье борьба с гнилой битой с типами .

Однако это создает очень сложную сигнатуру, которая не отражает простоту использования метода, ибольшую часть времени делает очень плохую документацию (обычно вам придется искать, какой CanBuildFrom в неявной области будет работать).Это обсуждалось в самом известном вопросе о переполнении стека .Таким образом, инструмент scaladoc был расширен, так что может появиться более простая запись, соответствующая предполагаемому использованию.Если вы посмотрите на источник из GenTraversableLike, где представлена ​​процедура, вы увидите следующее в скалярном файле для карты (и аналогичный во многих методах)

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

Подтипы добавляются в их документ @define Coll <className>, и карта (среди прочих) появляется с упрощенной подписью, помеченной [Вариант использования].В источнике из ArrayOps есть @define Coll ArrayOps, где он должен быть Array.

2 голосов
/ 27 ноября 2011

Вы можете использовать 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]].Затем, как пояснил Дидьер, исходная подпись для map, а не упрощенная подпись, указывает, что выведенный тип возвращаемого значения будет Array[Int]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...