2013-12-11 26 views
1

Я изучаю scala с целью научного программирования. Я пытаюсь написать простой общий код, используя spire. У меня есть следующий пример:Получение опыта работы в шпиле

import spire.algebra._ 
import spire.math._ 
import spire.implicits._ 
object TestSqrt { 
    def testSqrt[T: Numeric](x: T) = { 
     sqrt(x) 
    } 

    def main(args: Array[String]){ 
     val x_d: Double = 4.0 
     println(testSqrt(x_d)) 

     val x_i: Int = 4 
     println(testSqrt(x_i)) 
    } 
} 

Отпечаток 2.0 и 2, как и ожидалось. Проблема в том, что я не могу получить то же самое для работы с функцией exp. Следующий код не компилируется:

import spire.algebra._ 
import spire.math._ 
import spire.implicits._ 
object TestExp { 
    def testExp[T: Numeric](x: T) = { 
     exp(x) 
    } 

    def main(args: Array[String]){ 
     val x_d: Double = 4.0 
     println(testExp(x_d)) 

     val x_i: Int = 4 
     println(testExp(x_i)) 
    } 
} 

компилятор говорит:

... could not find implicit value for parameter t: spire.algebra.Trig[T] 
[error]   exp(x) 
[error]   ^ 
[error] one error found 
[error] (compile:compile) Compilation failed 

я делаю что-то неправильно, это ехр пока не поддерживается, или это ошибка? Может ли кто-нибудь представить рабочий пример использования exp с общим числовым типом из spire?

UPDATE:

я могу сделать ехр работу с помощью Триг вместо ввода цифр, например:

import spire.algebra._ 
import spire.math._ 
import spire.implicits._ 
object TestExp { 
    def testExp[T: Trig](x: T) = { 
     exp(x) 
    } 

    def main(args: Array[String]){ 
     val x_d: Double = 1.0 
     println(testExp(x_d)) 

     val x_f: Float = 1.0f 
     println(testExp(x_f)) 
     // val x_i: Int = 4 
     // println(testExp(x_i)) 
    } 
} 

Однако, он не работает с Int, только плавающих типов. Кроме того, если я использую Trig, то я не могу использовать sqrt. Следующий код не компилируется:

}

import spire.algebra._ 
import spire.math._ 
import spire.implicits._ 
object TestSqrt { 
    def testSqrt[T: Trig](x: T) = { 
     sqrt(x) 
    } 

    def main(args: Array[String]){ 
     val x_d: Double = 4.0 
     println(testSqrt(x_d)) 

     val x_i: Int = 4 
     println(testSqrt(x_i)) 
    } 
} 

И выдает ошибку:

... could not find implicit value for evidence parameter of type spire.algebra.Trig[Int] 
[error]   println(testSqrt(x_i)) 
[error]      ^
[error] two errors found 
[error] (compile:compile) Compilation failed 

Что я должен делать, если я хочу, как ехр и SQRT, и есть способ сделать работать с интегральными типами?

ответ

3

Типы классов имеют одинаковый тип для операндов и результатов операций. То есть, когда вы запустите sqrt по значению типа A, результат будет также иметь тип A.

Теперь вы можете утверждать, что sqrt на целые числа дают целые числа для некоторых значений входных значений, например 4, которые вы используете в своем примере. С другой стороны, что должно вернуть sqrt(5)? Кажется, он вернет наибольшее целое число, меньшее, чем фактический квадратный корень:

def testSqrt[T: Numeric](x: T): T = sqrt(x) 

scala> testSqrt(5) 
res0: Int = 2 

Это вряд ли то, что вы хотите. Интересно, что при вызове функции sqrt непосредственно, он будет возвращать Double:

scala> sqrt(5) 
res1: Double = 2.23606797749979 

Причина заключается в том, что при импорте spire.math._ вы получаете перегруженный sqrt функцию:

final def sqrt(x: Double): Double = Math.sqrt(x) 
final def sqrt[A](a: A)(implicit ev: NRoot[A]): A = ev.sqrt(a) 

Это «решает» проблему, задав приоритет первого варианта (поскольку он не требует неявных параметров). Что происходит в вашем примере, так это то, что вы используете общую подпись, поэтому будет вызываться вторая версия sqrt. Аргумент NRoot косвенно предоставляется, потому что вы говорите, что у вас есть Numeric.

Итак, вам нужно будет решить, что вы собираетесь делать - хотите ли вы вернуться к Double при расчете квадратного корня или пойти с этой довольно нетрадиционной идеей потенциального обрезания результата?


Теперь, чтобы выразить свое воззрение. В math, там снова перегружен версии exp, среди них:

final def exp(n: Double): Double = Math.exp(n) 
final def exp[A](a: A)(implicit t: Trig[A]): A = t.exp(a) 

Отличие sqrt в том, что вы не получите Trig[Int] ни при каких обстоятельствах. Нет никакого смысла.

Давайте определим функцию, которая может вызывать sqrt в exp на основе классов типа:

def sqrtAndExp[T: NRoot: Trig](x: T): (T, T) = (sqrt(x), exp(x)) 

Вы видите, вы можете использовать более чем один контекст, связанный в объявлении функции. Этот синтаксис эквивалентен

def sqrtAndExp[T](x: T)(implicit nroot: NRoot[T], trig: Trig[T]: (T, T) = 
    (sqrt(x), exp(x)) 

Для двойников, эта функция работает (с учетом того как вы импортировали spire.implicits._):

scala> sqrtAndExp(5.0) 
res2: (Double, Double) = (2.23606797749979,148.4131591025766) 

Но не для целых чисел:

scala> sqrtAndExp(5) 
<console>:18: error: could not find implicit value for evidence parameter of type 
        spire.algebra.Trig[Int] 
       sqrtAndExp(5) 
         ^
всегда можно использовать

Целые где требуется удвоение. Поэтому вы можете заставить его работать:

scala> sqrtAndExp[Double](5) 
res3: (Double, Double) = (2.23606797749979,148.4131591025766) 
+0

Очень ясно и решает мою проблему. Я понятия не имел, что вы можете использовать несколько границ контекста. – jcrudy

+0

Очевидно, вы также можете добавить перегруженную функцию 'def sqrtAndExp (x: Double): (Double, Double)', если вы хотите иметь возможность называть ее целыми литералами. –

Смежные вопросы