2016-10-28 26 views
2

我想用一些靈活的方法實現一些通用的數學函數。 例如一個名爲meandot函數聲明爲類似如何減少泛型方法中不需要的類型參數?

object Calc { 
    def meandot[..](xs: Array[Left], ys: Array[Right])(implicit ..): Result 
} 

其中meandot(xs, ys) = sum(x*y for x, y in zip(xs, ys))/length

當我調用meandot無特殊類型的參數,它應該默認類型返回一個值。例如

scala> Calc.meandot(Array(1, 2), Array(1, 1)) 
res0: Int = 1 

如果我調用meandot與特殊類型的參數,它可以返回一個適當的值。

scala> Calc.meandot[Int, Int, Double](Array(1, 2), Array(1, 1)) 
res1: Double = 1.5 

然而,在上面的前兩個類型參數是冗餘。我需要專門的唯一類型是返回類型。我想調用它簡化爲

scala> Calc.meandot2(Array(1, 2), Array(1, 1)) 
res2: Int = 1 

scala> Calc.meandot2[Double](Array(1, 2), Array(1, 1)) 
res3: Double = 1.5 

而且我找到了一種方法來實現它爲下面的代碼,它使用的是代理類MeanDotImp但它似乎不那麼優雅。所以我想知道是否有更好的解決方案來減少泛型方法中不需要的類型參數?

trait Times[L, R, N] { 
    def times(x: L, y: R): N 
} 

trait Num[N] { 
    def zero: N = fromInt(0) 
    def one: N = fromInt(1) 
    def fromInt(i: Int): N 
    def plus(x: N, y: N): N 
    def div(x: N, y: N): N 
} 

abstract class LowTimesImplicits { 
    implicit val IID: Times[Int, Int, Double] = new Times[Int, Int, Double] { 
    def times(x: Int, y: Int): Double = x * y 
    } 
} 

object Times extends LowTimesImplicits { 
    implicit val III: Times[Int, Int, Int] = new Times[Int, Int, Int] { 
    def times(x: Int, y: Int): Int = x * y 
    } 
} 

object Num { 
    implicit val INT: Num[Int] = new Num[Int] { 
    def fromInt(i: Int): Int = i 
    def plus(x: Int, y: Int): Int = x + y 
    def div(x: Int, y: Int): Int = x/y 
    } 

    implicit val DOU: Num[Double] = new Num[Double] { 
    def fromInt(i: Int): Double = i 
    def plus(x: Double, y: Double): Double = x + y 
    def div(x: Double, y: Double): Double = x/y 
    } 
} 

object Calc { 
    def meandot[L, R, N](xs: Array[L], ys: Array[R]) 
      (implicit t: Times[L, R, N], n: Num[N]): N = { 
    val total = (xs, ys).zipped.foldLeft(n.zero){ 
      case(r, (x, y)) => n.plus(r, t.times(x, y)) 
     } 
    n.div(total, n.fromInt(xs.length)) 
    } 

    implicit class MeanDotImp[L, R](val marker: Calc.type) { 
    def meandot2[N](xs: Array[L], ys: Array[R]) 
       (implicit t: Times[L, R, N], n: Num[N]): N = { 
     val total = (xs, ys).zipped.foldLeft(n.zero){ 
      case(r, (x, y)) => n.plus(r, t.times(x, y)) 
      } 
     n.div(total, n.fromInt(xs.length)) 
    } 
    } 
} 
+0

所以,你希望能夠調用'meandot [雙](...)',但仍保持開放調用'meandot [是int,short,浮點型](的選項。 ..)'(或某些這樣的)。這是這個想法嗎? – jwvh

+0

@jwvh即使我只能調用'meandot [Double](Array [L],Array [R])'',其中'L,R'可以是'Int,Float,Double'等,也可以。 – Eastsun

+0

如果你'重新做大量的數學,考慮https://github.com/non/spire – Reactormonk

回答

2

另一種解決方案是與你相似,但有一點更簡單:它首先解決了,你希望能夠設置類型參數,然後推斷其他兩個。爲了實現這一目標,我們可以聲明一個類apply方法:

class meandot[N] { 

    def apply[L, R](xs: Array[L], ys: Array[R]) 
    (implicit t: Times[L, R, N], n: Num[N]): N = ??? // your implementation 
} 

現在,爲了避免寫new meandot,我們可以定義剛剛實例化這個類中的方法:

object Calc { 

    def meandot[N]: meandot[N] = new meandot[N] 
} 

這種方法的優雅是值得商榷,但它非常簡單並且不涉及暗示。下面是一個使用演示:

scala> Calc.meandot(Array(1,2,3), Array(4,5,6)) 
res0: Int = 10 

scala> Calc.meandot[Double](Array(1,2,3), Array(4,5,6)) 
res1: Double = 10.666666666666666 
+0

我更喜歡你的解決方案。 – Eastsun

相關問題