Reputation: 633
I searched for a while and I was not able to find a clear and satisfying answer to this question.
Suppose I have this little function to compute the mean value of an Array:
def meanofArray(s:Array[Double]) : Double = s.sum/s.length
the problem is that is Array has to be Double. If for example I have this Array:
val x = Array( 1 , 2 , 3 , 4 )
and make the call :
println(meanofArray(x))
I get the error found : Array[Int] required: Array[Double]
How can I implement meanofArray so as to be able to accept any type of Int , Long , Float and Double with no errors ?
My thought was Generic types :
def meanofArray(s:Array[T]) : Double = s.sum/s.length
but I could not get it working.
Any other idea is welcomed !
Upvotes: 2
Views: 86
Reputation: 2913
The issue you are running into demonstrates the beauty of type abstraction.
Based solely on the signature of the function you provided
def meanofArray(s:Array[T]) : T
we know quite a bit about what this function can and can not do, as long as it does not do shady things like reflection of course. It can not do things like sort the Array, find the sum, etc. Based on some unfortunate characteristics of Scala, there are some things which we can not be sure it does not do, like use equals
or toString
for some T
.
You need to declare some additional information about the type T
in the function signature.
def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double
Here we are declaring that T
must have a definition of Numeric[T]
defined and in scope at call sites. Numeric[T]
provides the definitions of functions required in order to do the calculations you need. We are now declaring that this function may do more things like sort the array or find the sum.
And the implementation you are looking for...
def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double =
s.view.map(ev.toDouble).sum / s.length
EDIT: Map s
to view of Array[Double]
to decrease likelihood of overflow. Taken from @Eastsun's answer. Taking Numeric[T]
implicitly is strongly preferred over implicit view from T => Double
in my opinion though.
Upvotes: 3
Reputation: 2431
This is the cleanest solution i could come up for your question.
def mean[A <% Double : Numeric](seq:Seq[A]) = seq.sum/seq.length
Here, Seq.sum needs an evidence of A conforms to be a Numeric type. (:
in the type parameter)
And that division to work, the result of seq.sum
should be viewable as a Double. Altogether this results a result mean of Double
type
Upvotes: 0
Reputation: 18869
Here is what you want(The parameter's type of mean
is Seq[A]
so that you can use it as mean(List(1, 2, 3))
or mean("ABCDEFG")
):
scala> def mean[A](arr: Seq[A])(implicit view: A => Double): Double =
(arr map view sum) / arr.size
mean: [A](arr: Seq[A])(implicit view: A => Double)Double
scala> mean(Array(1, 2, 3, 4))
res0: Double = 2.5
Compare with @drstevens 's solution:
scala> def meanofArray[T](s: Array[T])(implicit ev: Numeric[T]): Double =
| ev.toDouble(s.sum) / s.length
meanofArray: [T](s: Array[T])(implicit ev: scala.math.Numeric[T])Double
scala> meanofArray(Array[Byte](111, 111, 111, 111))
res3: Double = -17.0 //Wrong!!!
scala> mean((Array[Byte](111, 111, 111, 111)))
res4: Double = 111.0 //Right !!!
Upvotes: 1