Se si desidera utilizzare solo la libreria standard scala, consultare Numeric[T]
. Nel tuo caso, dal momento che si desidera eseguire una divisione non intera, è necessario utilizzare la sottoclasse Fractional[T]
di Numeric
.
Ecco come apparirà il codice utilizzando tipeclass di scala standard. Notare che Fractional
si estende da Ordered
. Questo è conveniente in questo caso, ma non è matematicamente generico. Per esempio. non è possibile definire un per Complex
perché non è ordinato.
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
}
}
Tuttavia, per gravi calcoli numerici generici vorrei suggerire di dare un'occhiata a spire. Fornisce una gerarchia molto più elaborata di esemplari numerici. I typeclass Spire sono anche specializzati e quindi spesso veloci quanto lavorare direttamente con le primitive.
Ecco come utilizzare esempio apparirebbe con guglia:
// 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.
// E.g. 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 fornisce anche la conversione automatica da interi a T
se esiste un Field[T]
, così si potrebbe anche scrivere l'esempio come questo (quasi identico a la versione non generica). Tuttavia, penso che l'esempio sopra sia più facile da capire.
// 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
}
}
Aggiornamento: spire è molto potente e generico, ma può anche essere un po 'di confusione per un principiante. Soprattutto quando le cose non funzionano. Ecco uno excellent blog post che spiega l'approccio di base e alcuni dei problemi.
OK grazie controllerò spire. Il fatto è che lo scala docs per Int (http://www.scala-lang.org/api/current/index.html#scala.Int) non elenca i tratti che estende, quindi ho finito per indovinare su '. Come posso scoprire quali tratti 'Int' si estendono in modo da poter trovare' Numeric [T] 'per me stesso? – jbrown
@jbrown L'ordine è un [typeclass] (http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html). I typeclasses non funzionano con l'ereditarietà. –
Quindi, come avrebbe potuto sapere, guardando i documenti, quali tipi di lettere si applicano a Int? – jbrown