【问题标题】:How to make a function to work with a collection of any Numeric type in Scala如何使函数与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)
 }}

【问题讨论】:

    标签: scala functional-programming


    【解决方案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的代码

      【讨论】:

        猜你喜欢
        • 2021-10-14
        • 1970-01-01
        • 2021-01-08
        • 2014-09-16
        • 1970-01-01
        • 2013-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多