Scala:通用函数乘以不同类型的数字 [英] Scala: generic function multiplying Numerics of different types

查看:147
本文介绍了Scala:通用函数乘以不同类型的数字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图写一个通用的加权平均函数。
我想放宽对相同类型的值和权重的要求。即我想支持如下的序列:(value:Float,weight:Int)(value:Int,weight:Float) code>参数,而不仅仅是:(value:Int,weight:Int)



,我首先需要实现一个函数,它采用两个通用数值并返回它们的产品。 (B,y:A):(A,B)(b)数字,B:数字:??? = {...}

编写签名并思考返回类型,让我意识到我需要为Numerics定义某种分层结构来确定返回类型。即 x:Float * y:Int = z:Float x:Float * y:Double = z:Double
$ b 现在,Numeric类定义了操作加上等仅适用于相同类型的参数。我想我需要实现一个类型:

 类NumericConverter [Numeirc [A],Numeric [B]] {
类型BiggerType = ???
}

这样我可以将我的时间函数写成:

  def times [A:数字,B:数字](x:B,y:A):(A,B):
NumericConverter [Numeirc [A],Numeric [B]]。BiggerType = {...}

较小的类型为较大的一个,并将它提供给 times()



正确的轨道?我该如何实施 BiggerType



显然我不能这样做:

  type myType = if(...)Int else Float 

,因为它是动态评估的,所以它不会工作。



我明白我可以做到这一点使用Scalaz等,但这是一个学术练习,我想了解如何编写一个基于参数类型静态返回类型的函数



请随时告诉我是否有更简单的方法。



更新



这就是我想出来的。

  abstract class NumericsConvert [A:Numeric,B:Numeric] {

def AisBiggerThanB:Boolean

def timesA = new PartialFunction [(A,B),A ] {
覆盖def isDefinedAt(x:(A,B)):Boolean = AisBiggerThanB
覆盖def apply(x:(A,B)):A =隐式地[Numeric [A]]。times (x._1,x._2.asInstanceOf [A])
}

def timesB = new PartialFunction [(A,B),B] {
override def isDefinedAt x:(A,B)):Boolean =!AisBiggerThanB
override def apply(x:(A,B)):B = implicitly [Numeric [B]] .times(x._1.asInstanceOf [B] ,x._2)
}
def times:PartialFunction [(A,B),Any] = timesA orElse timesB
}

def times [A:Numeric ,B:数字](x:B,y:A)=隐式地[NumericsConvert [A,B]]。times(x,y)


这很愚蠢,因为我将不得不为两者创建含义

  IntDouble扩展NumericsConvert [Int,Double] 

  DoubleInt扩展NumericsConvert [Double,Int] 

更不用说次数的返回类型现在是任何,但无论如何,我的时代功能。我想我会在这里添加它,以免它可能有助于达成解决方案。所以侧面的问题:我如何能够将一个类/函数的上下文绑定类型传递给另一个,就像我正在尝试做的那样。

解决方案

我认为你做得比它需要的更难。



你需要证据表明这两个参数都是数字。随着证据的确立,这项工作就完成了。 Scala将采用数字扩大化结果是两种接收类型中更一般的。

  def mult [T](a:T,b:T )(implicit ev:Numeric [T]):T = 
ev.times(a,b)

如果你想获得更多的鸽友,你可以引入所需的暗示。然后阅读和理解起来更容易一些。



pre $ def $ mult [T:Numeric](a:T,b:T) :T = {
import Numeric.Implicits._
a * b
}

证明:

  mult(2.3f,7)// res0:Float = 16.1 
mult 8,2.1)// res1:Double = 16.8
mult(3,2)// res2:Int = 6

有关泛型类型和数值范围扩展的更多信息,请这个问题及其答案值得研究。


I am trying to write a generic weighted average function. I want to relax the requirements on the values and the weights being of the same type. ie, I want to support sequences of say: (value:Float,weight:Int) and (value:Int,weight:Float) arguments and not just: (value:Int,weight:Int)

To do so, I first need to implement a function that takes two generic numerical values and returns their product.

def times[A: Numeric, B: Numeric](x: B, y: A): (A, B) : ??? = {...}

Writing the signature and thinking about the return type, made me realise that I need to define some sort of hierarchy for Numerics to determine the return type. ie x:Float*y:Int=z:Float, x:Float*y:Double=z:Double.

Now, Numeric class defines operations plus, times, etc. only for arguments of the same type. I think I would need to implement a type:

class NumericConverter[Numeirc[A],Numeric[B]]{
type BiggerType=???
}

so that I can write my times function as:

def times[A: Numeric, B: Numeric](x: B, y: A): (A, B) :
NumericConverter[Numeirc[A],Numeric[B]].BiggerType= {...}

and convert the "smaller type" to the "bigger one" and feed it to times().

Am I on the right track? How would I "implement" the BiggerType?

clearly I can't do something like:

type myType = if(...) Int else Float

as that is evaluated dynamically, so it worn't work.

I understand that I might be able to do this Using Scalaz, etc. but this is an academic exercise and I want to understand how to write a function that statically returns a type based on the argument types.

Feel free to let me know if there is a whole easier way of doing this.

update:

this is what I came up with it.

abstract class NumericsConvert[A: Numeric,B: Numeric]{

    def AisBiggerThanB: Boolean

    def timesA=new PartialFunction[(A,B), A] {
        override def isDefinedAt(x: (A, B)): Boolean = AisBiggerThanB
        override def apply(x: (A, B)): A = implicitly[Numeric[A]].times(x._1, x._2.asInstanceOf[A])
    }

    def timesB=new PartialFunction[(A,B), B] {
        override def isDefinedAt(x: (A, B)): Boolean = !AisBiggerThanB
        override def apply(x: (A, B)): B = implicitly[Numeric[B]].times(x._1.asInstanceOf[B], x._2)
    }
    def times: PartialFunction[(A, B), Any] = timesA orElse timesB
}

def times[A: Numeric, B: Numeric](x: B, y: A)= implicitly[NumericsConvert[A,B]].times(x,y)

which is silly as I will have to create implicits for both

IntDouble extends NumericsConvert[Int,Double]

and

DoubleInt extends NumericsConvert[Double,Int]

not to mention that the return type of times is now Any, but regardless, I am getting errors for my times functions. I thought I would add it here in case it might help with arriving at a solution. so side question: how I can pass context bound types of one class/function to another like I am trying to do in times.

解决方案

I think you're making this harder than it needs to be.

You need "evidence" that both parameters are Numeric. With that established let the evidence do the work. Scala will employ numeric widening so that the result is the more general of the two received types.

def mult[T](a: T, b: T)(implicit ev:Numeric[T]): T =
  ev.times(a,b)

If you want to get a little fancier you can pull in the required implicits. Then it's a little easier to read and understand.

def mult[T: Numeric](a: T, b: T): T = {
  import Numeric.Implicits._
  a * b
}

Proof:

mult(2.3f , 7)  //res0: Float = 16.1
mult(8, 2.1)    //res1: Double = 16.8
mult(3, 2)      //res2: Int = 6

For more on generic types and numeric widening, this question, and its answer, are worth studying.

这篇关于Scala:通用函数乘以不同类型的数字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆