创建一个采用任何2D序列并将其转换为Scala中的数组[Array [_]]的方法

时间:2011-06-20 12:32:33

标签: scala type-conversion scala-collections

正如标题所述,我希望有一种方法可以应用于任何类型的参数,如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)))

为了完整性,这里是构造函数;也许这很重要,因为Manifest需要A

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)))
            ^

(失败)对于didierd的想法的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 个答案:

答案 0 :(得分:3)

好的,终于得到了一些干净简单的东西,不需要任何新的暗示,尽管这样做效率很低,因为它会从数组转换为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)

年纪较大,笨拙,但可能可能更有效的答案:

要正常工作,这显然需要一个相当重要的解决方案。一种方法是使用ArraySeqEither的类型联合进行编码:< / p>

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更安全。

另一种选择是使用Miles Sabin's type union operator。通过清单工作有点难看;这是一个实际上安全的版本,但编译器不知道它,所以必须进行强制转换:

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]]]
  }
}

总的来说,我会说第一个解决方案有点清洁。

答案 1 :(得分:2)

你的问题的一个重要部分是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&lt;%Seq [A]允许隐式转换。
  4. 除此之外,您忘记了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然后)因为如果你在NestedSeqToNestedArray之后传递一个参数,它期望它是隐式Manifest。

    也许值得一提,琐碎的解决方案,定义两个功能?

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