Создание метода, который берет любую 2D-последовательность и превращает ее в Array [Array [_]] в Scala - PullRequest
5 голосов
/ 20 июня 2011

Как гласит заголовок, я хочу иметь метод, который можно применять к любому типу аргумента, например Array[Array[_]] или Seq[Array[_]] или Array[Seq[_]] или Seq[Seq[_]].Аргумент должен быть превращен в 2D-массив (Array[Array[_]]), таким образом, изменяя только тип участвующих коллекций.

У меня есть следующая подпись, по-видимому, принимающая любую такую ​​комбинацию, но я не могу построить массивы.

  def apply[A: Manifest, S[_] <: Seq[_], U <% S[S[A]]](components: U): CombinationIterator[A] = {
    new CombinationIterator(Array(components.map((s: S[A]) => s.toArray)))
  }

Класс CombinationIterator принимает в качестве аргумента Array[Array[T]].Я получаю следующую ошибку:

error: could not find implicit value for evidence parameter of type ClassManifest[A]
new CombinationIterator(Array(components.map((s: S[A]) => s.toArray)))

Для полноты, вот конструктор;возможно это имеет значение, потому что для A.

. *1018*
class CombinationIterator[A: Manifest](components: Array[Array[A]]) extends Iterator[Array[A]]

требуется неудачный сеанс REPL

. Следующее работает для Array[Seq[_]], но не для Seq[Array[_]]:

scala> def f[T:Manifest](s: Seq[Seq[T]]) = s.map(_.toArray).toArray
f: [T](s: Seq[Seq[T]])(implicit evidence$1: Manifest[T])Array[Array[T]]

scala> f(Array(Seq(1,2),Seq(3,4)))
res22: Array[Array[Int]] = Array(Array(1, 2), Array(3, 4))

scala> f(Seq(Array(1,2),Array(3,4)))
<console>:9: error: type mismatch;
 found   : Seq[Array[Int]]
 required: Seq[Seq[?]]
       f(Seq(Array(1,2),Array(3,4)))
            ^

(сбой) REPL для идеи Дидьера

scala> def f[T: Manifest, ST <% Seq[T]](s: Seq[ST]) = s.map(_.toArray).toArray
f: [T, ST](s: Seq[ST])(implicit evidence$1: Manifest[T], implicit evidence$2: (ST) => Seq[T])Array[Array[T]]

scala> f(Seq(Seq(1)))
<console>:9: error: No implicit view available from Seq[Int] => Seq[T].
       f(Seq(Seq(1)))
        ^

Решение Я установил

Следующий код работает для моего проекта.Может быть, не все частные случаи покрыты.Это вариант второго ответа Рекса.

Я чувствую, что последствия хорошо заключены в объект-компаньон.

object CombinationIterator {
  case class AArray[T](aa: Array[Array[T]])
  implicit def seqseq2AA[T: Manifest](ss: Seq[Seq[T]]) = AArray(ss.map(_.toArray).toArray)
  implicit def seqarray2AA[T: Manifest](sa: Seq[Array[T]]) = AArray(sa.toArray)

  def apply[T: Manifest](components : AArray[T]): CombinationIterator[T] = {
    new CombinationIterator(components.aa)
  }
}

Редактировать

Чтобы опубликовать более новое пониманиео причинах вопроса.Я хотел иметь эти вложенные массивы из соображений производительности.Но массивы более важны для примитивных типов.Так что, вероятно, не так уж и плохо - с точки зрения производительности - иметь внешний массив в качестве «правильной» структуры данных, такой как Vector.

Ответы [ 2 ]

3 голосов
/ 20 июня 2011

Ладно, наконец-то получил что-то чистое и простое, которое не требует новых последствий, хотя это неэффективно, поскольку он преобразуется из массива в Seq только для того, чтобы он мог вернуться обратно.Или вы можете использовать одно неявное, чтобы остаться с массивами:

Неявный ответ:

def ss2aa[A,B[_],C[_]](c: C[B[A]])(
  implicit b2seq: B[A] => Seq[A], c2seq: C[B[A]] => Seq[B[A]], ma: ClassManifest[A]
) = c2seq(c).map(b => b2seq(b).toArray).toArray

Более эффективный, с неявным:

implicit def seq2array[A: ClassManifest](sa: Seq[A]) = sa.toArray
def ss2aa[A,B[_],C[_]](c: C[B[A]])(
  implicit b2arr: B[A] => Array[A], c2arr: C[B[A]] => Array[B[A]], ma: ClassManifest[A]
) = c2arr(c).map(b2arr)

Старые, неуклюжие, но возможно возможно, более эффективные ответы:

Для правильной работы это, очевидно, требует довольно тяжелого решения. Одним из способов является кодирование объединения типовArray и Seq с использованием Either:

implicit def ss2leftleft[A](ssa: Seq[Seq[A]]) = Left(Left(ssa))
implicit def sa2leftright[A](saa: Seq[Array[A]]) = Left(Right(saa))
implicit def as2rightleft[A](asa: Array[Seq[A]]) = Right(Left(asa))
implicit def aa2rightright[A](aaa: Array[Array[A]]) = Right(Right(aaa))
def ss2aa[A: Manifest](
  x: Either[Either[Seq[Seq[A]],Seq[Array[A]]],Either[Array[Seq[A]],Array[Array[A]]]]
) = x match {
  case Left(Left(y)) => y.map(_.toArray).toArray
  case Left(Right(y)) => y.toArray
  case Right(Left(y)) => y.map(_.toArray)
  case Right(Right(y)) => y
}

Если вам так хочется, вы, конечно, можете определить свои собственные оболочки суперкласса и подкласса, которые делают то же самое.Возможно, безопаснее, чем использовать Either.

Другой вариант - использовать оператор объединения типов Майлза Сабина .Прорабатывать манифесты немного некрасиво;вот версия, которая на самом деле безопасна, но компилятор не знает об этом, поэтому приведение необходимо:

object Example {
  // Type union system from Miles Sabin (with non-Unicode names)
  type Not[A] = A => Nothing
  type Union[A,B] = Not[Not[A] with Not[B]]
  type Id[A] = Not[Not[A]]

  def ss2aa[A,B[_],C[_]](b: C[B[A]])(
    implicit ev: (Id[B[A]] <:< Union[Seq[A],Array[A]]),
    ev2: (Id[C[B[_]]] <:< Union[Seq[B[_]],Array[B[_]]]),
    ma: ClassManifest[A],
    mssa: ClassManifest[Seq[Seq[A]]],
    msaa: ClassManifest[Seq[Array[A]]],
    masa: ClassManifest[Array[Seq[A]]],
    mf: ClassManifest[C[B[A]]]
  ) = {
    if (mf <:< mssa) b.asInstanceOf[Seq[Seq[A]]].map(_.toArray).toArray
    else if (mf <:< masa) b.asInstanceOf[Array[Seq[A]]].map(_.toArray)
    else if (mf <:< msaa) b.asInstanceOf[Seq[Array[A]]].toArray
    else b.asInstanceOf[Array[Array[A]]]
  }
}

В целом, я бы сказал, что первое решение немного чище.

2 голосов
/ 20 июня 2011

Важной частью вашей проблемы является то, что Array неявно конвертируется в Seq, но не является Seq (определен в JVM, слишком поздно, чтобы добавлять предков сейчас).

В вашей первой проблеме вы неВаши общие параметры не должны быть такими сложными, и вы, возможно, выдвигаете вывод типа scala за пределы его возможностей.Я не могу сказать вам, где именно.Следующей подписи должно быть достаточно:

def apply[A : Manifest, SA  <% Seq[A]](components: Seq[SA])
  1. Нет необходимости искать общий тип для внешнего и внутреннего типа Seq.Так что S: Seq [_] бесполезен.
  2. Тебе все равно, что такое внешний тип, все, что ты делаешь - это работа над Seq, Seq достаточно.неявные преобразования тоже будут работать, поэтому с внешним массивом все будет в порядке.
  3. Поскольку Seq является ковариантным, и ваши операции над внутренним типом также являются операциями Seq.Seq [Seq [A]] может быть достаточно.Но вы хотите принять Array на этом этапе, следовательно, S <% Seq [A], чтобы разрешить неявное преобразование. </li>

Кроме того, вы забыли: _ * in Array(components.map((s: S[A]) => s.toArray): _*).Вы должны указать это явно, когда вы передаете Seq вместо повторяющихся аргументов.

С функцией F проблема снова в том, что Array не является Seq.То же решение:

def f[T: Manifest, ST <% Seq[T]](s: Seq[ST])

Редактировать: не работает .Нужно явно передать универсальный параметр, Scala не может их вывести.

Возможный прогресс, но не решение.При этом вам нужно только указать тип элементов, а не тип внутреннего Seq.Я надеюсь, что кто-то придет с лучшим ответом.

class NestedSeqToNestedArray[A : Manifest] {
   def on[SA <% Seq[A]](s: Seq[SA]) = s.map(_.toArray).toArray
}
object NestedSeqToNestedArray {
  def apply[A : Manifest] = new NestedSeqToNestedArray[A]
}

NestedSeqToNestedArray[Int].on(Seq(Array(1)))
res11: Array[Array[Int]] = Array(Array(1))

Вы не можете вызывать событие методом "on" apply (хорошо, вы можете, но тогда вам нужно написать apply), потому что если вы передадите аргумент сразу послеNestedSeqToNestedArray, он ожидает, что это будет неявный манифест.

Может быть, стоит упомянуть, тривиальное решение, определить две функции?

def f[A : Manifest](s: Seq[Seq[A]]) = s.map(_.toArray).toArray
def g[A : Manifest](s: Seq[Array[A]]) = s.map(_.clone).toArray
...