如何使函数与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)

适用于集合类型 ArrayListSeqVector 等。

适用于数字类型 DoubleFloatLongIntShortCharByte

如果你想返回与被求和相同的数字类型:

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 循环函数与自定义类一起使用

Scala - 中缀与点符号

如何使泛型代理类型与静态类型检查器一起使用

如何将一个集合分成两个子集,以使两个集合中数字之和之间的差异最小?

如何使此代码与 Integer-> Int 类型签名一起使用?