如何使函数与Scala中的任何数字类型的集合一起使用
Posted
技术标签:
【中文标题】如何使函数与Scala中的任何数字类型的集合一起使用【英文标题】:How to make a function to work with a collection of any Numeric type in Scala 【发布时间】:2021-04-19 21:39:55 【问题描述】:我想创建一个函数,它可以接受任何类型的 Collection: List、Array ... 并对其中的值求和。我尝试过这样的事情。但是使用 AnyVal 代替 Double 效果不佳
class SumNumbers
object SumNumbers
def sum(collection: LinearSeq[Double]): Double =
collection match
case Nil => 0.0
case head:: tail => head + sum(tail)
object MainT
def main(args: Array[String]): Unit =
val x = SumNumbers.sum(List(0, 1, 3, 4))
println(x)
【问题讨论】:
【参考方案1】:遵循您的基本设计。
def sum[N:Numeric](collection: Iterable[N]): Double =
import Numeric.Implicits._
if (collection.isEmpty) 0.0
else collection.head.toDouble + sum(collection.tail)
适用于集合类型 Array
、List
、Seq
、Vector
等。
适用于数字类型 Double
、Float
、Long
、Int
、Short
、Char
和 Byte
。
如果你想返回与被求和相同的数字类型:
def sum[N:Numeric](collection: Iterable[N]): N =
import Numeric.Implicits._
if (collection.isEmpty) implicitly[Numeric[N]].zero
else collection.head + sum(collection.tail)
【讨论】:
【参考方案2】:你基本上想要Polymorphism,也就是将同一个操作应用于多种类型的能力。
事实证明,在这种情况下,最好的方法之一是Typeclass,因为它可以灵活地接受外部类型。
如果您只想接受任何数字,那么您只需要使用作为标准库一部分的Numeric
类型类;正如已经显示的here & here。
但是,由于您还想对集合类型进行抽象,您可能需要使用 Seq
特征组合该集合类型以及子类型多态性。
或者,您还可以通过为集合提供类型类来将其提升到新的水平。
如果你愿意使用cats library,你可以只使用Monoid
作为组合部分,Foldable
作为迭代(折叠)部分。
import cats.Foldable, Monoid
import cats.syntax.all._
def usingCats[C[_] : Foldable, A : Monoid](data: C[A]): A =
data.combineAll
可以这样使用:
import scala.collection.immutable.ArraySeq
val ints = List(1, 2, 3)
val doubles = ArraySeq(0.0d, 5.0d, 10.0d)
val strings = LazyList("A", "B", "C")
usingCats(ints) // res: Int = 6
usingCats(doubles) // res: Double = 15.0
usingCats(strings) // res: String = ABC
但是,您也可以自己实现它:(但您会重复库已经提供的大量代码)
trait MyFoldable[C[_]]
def fold[A, B](ca: C[A])(z: B)(op: (B, A) => B): B
object MyFoldable
implicit final val MyFoldableList: MyFoldable[List] =
new MyFoldable[List]
override def fold[A, B](list: List[A])(z: B)(op: (B, A) => B): B =
list.foldLeft(z)(op)
implicit final val MyFoldableArraySeq: MyFoldable[ArraySeq] =
new MyFoldable[ArraySeq]
override def fold[A, B](arr: ArraySeq[A])(z: B)(op: (B, A) => B): B =
arr.foldLeft(z)(op)
implicit final val MyFoldableLazyList: MyFoldable[LazyList] =
new MyFoldable[LazyList]
override def fold[A, B](lazyList: LazyList[A])(z: B)(op: (B, A) => B): B =
lazyList.foldLeft(z)(op)
trait MyMonoid[A]
def empty: A
def combine(a1: A, a2: A): A
object MyMonoid
implicit final val MyMonoidInt: MyMonoid[Int] =
new MyMonoid[Int]
override final val empty: Int = 0
override final def combine(i1: Int, i2: Int): Int =
i1 + i2
implicit final val MyMonoidDouble: MyMonoid[Double] =
new MyMonoid[Double]
override final val empty: Double = 0.0d
override final def combine(d1: Double, d2: Double): Double =
d1 + d2
implicit final val MyMonoidString: MyMonoid[String] =
new MyMonoid[String]
override final val empty: String = ""
override final def combine(s1: String, s2: String): String =
s1 + s2
def byHand[C[_], A](data: C[A])
(implicit foldable: MyFoldable[C], monoid: MyMonoid[A]): A =
foldable.fold(data)(monoid.empty)(monoid.combine)
这会产生相同的结果:
byHand(ints) // res: Int = 6
byHand(doubles) // res: Double = 15.0
byHand(strings) // res: String = ABC
可以看到运行here的代码。
【讨论】:
以上是关于如何使函数与Scala中的任何数字类型的集合一起使用的主要内容,如果未能解决你的问题,请参考以下文章
如何使正则表达式与 perl 命令一起使用并从文件中提取数字?
如何使 C++ 中的 for each 循环函数与自定义类一起使用