gpt4 book ai didi

scala - 如何使用两种参数类型在 Scala 中实现泛型函数?

转载 作者:行者123 更新时间:2023-12-04 18:03:45 26 4
gpt4 key购买 nike

我想在 Scala 中实现一个函数来计算两个数字序列的点积,如下所示

val x = Seq(1,2,3.0)
val y = Seq(4,5,6)
val z = (for (a <- x; b <- y) yield a*b).sum
scala> z : Double = 90.0

val x = Seq(1,2,3)
val y = Seq(4,5,6)
val z = (for (a <- x; b <- y) yield a*b).sum
scala> z : Int = 90

请注意,如果两个序列的类型不同,则结果为 Double。如果两个序列的类型相同(例如 Int),则结果为 Int。

我想出了两个备选方案,但都不符合上面定义的要求。

备选方案#1:

def dotProduct[T: Numeric](x: Seq[T], y: Seq[T]): T = (for (a <- x; b <- y) yield implicitly[Numeric[T]].times(a, b)).sum

这会返回与输入相同类型的结果,但不能采用两种不同的类型。

备选方案 #2:

def dotProduct[A, B](x: Seq[A], y: Seq[B])(implicit nx: Numeric[A], ny: Numeric[B]) = (for (a <- x; b <- y) yield nx.toDouble(a)*ny.toDouble(b)).sum

这适用于所有数字序列。但是,它总是返回一个 Double,即使这两个序列的类型是 Int。

非常感谢任何建议。

附注我上面实现的函数不是“点积”,而是两个序列的乘积之和。感谢 Daniel 指出。

备选方案 #3(略优于备选方案 #1 和 #2):

def sumProduct[T, A <% T, B <% T](x: Seq[A], y: Seq[B])(implicit num: Numeric[T]) = (for (a <- x; b <- y) yield num.times(a,b)).sum

sumProduct(Seq(1,2,3), Seq(4,5,6)) //> res0: Int = 90
sumProduct(Seq(1,2,3.0), Seq(4,5,6)) //> res1: Double = 90.0
sumProduct(Seq(1,2,3), Seq(4,5,6.0)) // Fails!!!

不幸的是,View Bound 特性(例如“<%”)将在 Scala 2.10 中被弃用。

最佳答案

您可以创建一个表示促销规则的类型类:

trait NumericPromotion[A, B, C] {
def promote(a: A, b: B): (C, C)
}

implicit object IntDoublePromotion extends NumericPromotion[Int, Double, Double] {
def promote(a: Int, b: Double): (Double, Double) = (a.toDouble, b)
}

def dotProduct[A, B, C]
(x: Seq[A], y: Seq[B])
(implicit numEv: Numeric[C], promEv: NumericPromotion[A, B, C])
: C = {
val foo = for {
a <- x
b <- y
} yield {
val (pa, pb) = promEv.promote(a, b)
numEv.times(pa, pb)
}

foo.sum
}

dotProduct[Int, Double, Double](Seq(1, 2, 3), Seq(1.0, 2.0, 3.0))

我的 typeclass-fu 不足以消除调用 dotProduct 时的显式类型参数,我也无法弄清楚如何避免 val foo方法;内联 foo 导致编译器错误。我把这归因于没有真正内化隐式解析规则。也许其他人可以让您走得更远。

还值得一提的是,这是定向的;你无法计算 dotProduct(Seq(1.0, 2.0, 3.0), Seq(1, 2, 3))。但这很容易修复:

implicit def flipNumericPromotion[A, B, C]
(implicit promEv: NumericPromotion[B, A, C])
: NumericPromotion[A, B, C] =
new NumericPromotion[A, B, C] {
override def promote(a: A, b: B): (C, C) = promEv.promote(b, a)
}

还值得一提的是,您的代码不计算点积。 [1, 2, 3][4, 5, 6] 的点积是 4 + 10 + 18 = 32

关于scala - 如何使用两种参数类型在 Scala 中实现泛型函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30770552/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com