How to define a function as common to all numbers in scala?

I thought that I needed to parameterize my function in all types of Ordering[_] . But that does not work.

How can I do the following function for all types that support the required mathematical operations, and how could I find this on my own?

  /** * Given a list of positive values and a candidate value, round the candidate value * to the nearest value in the list of buckets. * * @param buckets * @param candidate * @return */ def bucketise(buckets: Seq[Int], candidate: Int): Int = { // x <= y buckets.foldLeft(buckets.head) { (x, y) => val midPoint = (x + y) / 2f if (candidate < midPoint) x else y } } 

I tried to click on the mathematical operators ( / , + ) in intellij, but just got a Sc synthetic function notification.

+5
source share
1 answer

If you want to use only the standard scala library, see Numeric[T] . In your case, since you want to do non-integer division, you will need to use the Fractional[T] Numeric subclass.

Here's what the code would look like using standard scala style classes. Note that Fractional continues from Ordered . This is convenient in this case, but it is also not mathematically general. For instance. you cannot define Fractional[T] for Complex because it is not ordered.

 def bucketiseScala[T: Fractional](buckets: Seq[T], candidate: T): T = { // so we can use integral operators such as + and / import Fractional.Implicits._ // so we can use ordering operators such as <. We do have a Ordering[T] // typeclass instance because Fractional extends Ordered import Ordering.Implicits._ // integral does not provide a simple way to create an integral from an // integer, so this ugly hack val two = (implicitly[Fractional[T]].one + implicitly[Fractional[T]].one) buckets.foldLeft(buckets.head) { (x, y) => val midPoint = (x + y) / two if (candidate < midPoint) x else y } } 

However, for serious general computing, I would suggest taking a look at spire . It provides a much more complex hierarchy of number types. Spire typeclasses are also specialized and therefore often as fast as working directly with primitives.

Here's how to use the spire example:

 // imports all operator syntax as well as standard typeclass instances import spire.implicits._ // we need to provide Order explicitly, since not all fields have an order. // Eg you can define a Field[Complex] even though complex numbers do not // have an order. def bucketiseSpire[T: Field: Order](buckets: Seq[T], candidate: T): T = { // spire provides a way to get the typeclass instance using the type // (standard practice in all libraries that use typeclasses extensively) // the line below is equivalent to implicitly[Field[T]].fromInt(2) // it also provides a simple way to convert from an integer // operators are all enabled using the spire.implicits._ import val two = Field[T].fromInt(2) buckets.foldLeft(buckets.head) { (x, y) => val midPoint = (x + y) / two if (candidate < midPoint) x else y } } 

Spire even provides automatic conversion from integers to T if Field[T] exists, so you can even write an example like this (almost identical to the non-universal version). However, I think the above example is easier to understand.

 // this is how it would look when using all advanced features of spire def bucketiseSpireShort[T: Field: Order](buckets: Seq[T], candidate: T): T = { buckets.foldLeft(buckets.head) { (x, y) => val midPoint = (x + y) / 2 if (candidate < midPoint) x else y } } 

Update: The spire is very powerful and general, but can also be somewhat confusing for beginners. Especially when something is not working. Here is a great blog post explaining the basic approach and some of the problems.

+9
source

Source: https://habr.com/ru/post/1233507/


All Articles