2015-10-12 19 views
5

I thought Potrzebowałem sparametryzować moją funkcję we wszystkich typach Ordering[_]. Ale to nie działa.Jak zdefiniować funkcję jako ogólną dla wszystkich liczb w scala?

W jaki sposób mogę sprawić, aby następująca funkcja działała dla wszystkich typów, które obsługują wymagane działania matematyczne i jak mogłem to samemu znaleźć?

/** 
    * 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 
    } 
    } 

Próbowałem polecenia klikając na operatorów matematycznych (/, +) w IntelliJ, ale tylko dostał zawiadomienie Sc synthetic function.

Odpowiedz

9

Jeśli chcesz użyć tylko standardowej biblioteki scala, spójrz na Numeric[T]. W twoim przypadku, ponieważ chcesz zrobić podział niecałkowity, będziesz musiał użyć podklasy Fractional[T] z Numeric.

Oto, jak kod wyglądałby przy użyciu standardowych czcionek biblioteki scala. Zauważ, że Fractional rozciąga się od Ordered. Jest to wygodne w tym przypadku, ale nie jest również matematycznie ogólne. Na przykład. nie można zdefiniować wartości Fractional[T] dla Complex, ponieważ nie jest ona uporządkowana.

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

Jednak dla poważnych ogólnych obliczeń numerycznych Chciałbym zasugerować przyjrzeniu spire. Zapewnia znacznie bardziej rozbudowaną hierarchię liczbowych typów. Typy maszyn iglica są również wyspecjalizowane, a zatem często tak szybkie, jak praca bezpośrednio z prymitywami.

Oto jak używać przykład wyglądałby za pomocą iglicy:

// 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 nawet zapewnia automatyczną konwersję z liczb całkowitych do T jeśli istnieje Field[T], więc można nawet napisać przykład tak (prawie identyczną wersja nietypowa). Myślę jednak, że powyższy przykład jest łatwiejszy do zrozumienia.

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

Aktualizacja: iglica jest bardzo potężna i generyczna, ale może również wprowadzać w błąd początkującego. Zwłaszcza, gdy coś nie działa. Oto excellent blog post wyjaśniające podstawowe podejście i niektóre z problemów.

+0

OK, dzięki temu sprawdzę iglicę. Chodzi o to, że scala docs dla Int (http://www.scala-lang.org/api/current/index.html#scala.Int) nie wymienia cech, które rozszerza, więc doszedłem do zgadywania o 'Zamawianiu '. Jak mogę się dowiedzieć, jakie cechy rozszerza "Int", aby móc znaleźć "Numeric [T]" dla siebie? – jbrown

+0

@jbrown Ordering to [typeclass] (http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html). Typy nie działają z dziedziczeniem. –

+0

W jaki sposób mogłem wiedzieć, patrząc na dokumenty, jakie typologie odnoszą się do Int? – jbrown

Powiązane problemy