سؤال

I've read up a little on Manifests and the erasure-avoidance techniques required to allow Scala to do things like "new Array[Array[T]]", but I'm a little stumped with this one....

I've got a method that sort of tabulates up a bunch of rows in an array like a spreadsheet. For example imagine a 2D array like this:

11,   5,    4
8,    3,    7
2,    1,    4

I wrote a method that sums up the columns of that array and spits out a 1D array like [21, 9, 15]

I want to genericize it beyond just Ints (like Doubles or Floats maybe), and when I add a parameter and manifest I get a compile error.

Here's the code

def sumGrid[T](grid: Array[Array[T]])(implicit m: ClassManifest[T]): Array[T] = {

  val sum = new Array[T](grid(0).size)

  for(i <- 0 until grid.size) {
    for(j <- 0 until grid(0).size) {
      sum(j) = sum(j) + grid(i)(j)
    }
  }  
  sum
}

Here's the compile error:

[ERROR] ...scala/euler/GridOperations.scala:126: error: type mismatch;
[INFO]  found   : T
[INFO]  required: String
[INFO]         sum(j) = sum(j) + grid(i)(j)
[INFO]                                  ^
[ERROR] one error found

What's going on here? Why is String "required"?

هل كانت مفيدة؟

المحلول

Because you use the + operator which is always defined for strings. Any type can be converted to String (toString is always defined) and thus it will work for any type T.

But you could add some constraints on T to ensure it corresponds to the arithmetic operation. For instance, you can use implicits to get a Numeric objects defining the addition for type T:

def sumGrid[T](grid: Array[Array[T]])
(implicit m: ClassManifest[T], num: Numeric[T]): Array[T] = {

  val sum = new Array[T](grid(0).size)

  for(i <- 0 until grid.size) {
    for(j <- 0 until grid(0).size) {
      sum(j) = num.plus( sum(j), grid(i)(j) )
    }
  }  
  sum
}

نصائح أخرى

You handled the Array part, but T doesn't have any constrains that say it has a + method. Because of that, Scala thinks you are adding strings -- it will auto-convert any type to a String when you add it to another String. So it auto-converted sum(j) to String, and now expects grid(i)(j) to be another String, at which point it gives up and fails.

You can handle the thing about T being numeric in a similar manner to the ClassManifest stuff. Here:

// I'm using the "context bound" notation, to make the declaration shorter and
// more obvious on what I'm saying about T
def sumGrid[T : ClassManifest : Numeric](grid: Array[Array[T]]): Array[T] = {
  // the following line, plus the Numeric bound, makes T usable as a number
  import scala.math.Numeric.Implicits._

  val sum = new Array[T](grid(0).size)

  for(i <- 0 until grid.size) {
    for(j <- 0 until grid(0).size) {
      sum(j) = sum(j) + grid(i)(j)
    }
  }  
  sum
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top