我如何比较两个数字。 任何数字。 像Int
和Float
? 我没有兴趣与复数之类的东西比较。 我只是想比较的,具有可比性。 Float和诠释的。
假设你有:
def compareTwoNumbers[???](array:Array[???], number:???) = {
array(0) > number // this has to compile
}
我写的东西,而不是???
?
事情到目前为止,我已经试过:
-
Number
。 -
T <: Number
-
Numeric
(对不起,我不知道如何在这种情况下使用它,没有例子/文档太差)。
我认为主要的问题是数字类型的转换。 所以let's编码:
trait NumericConversion[X, Y] {
def convert(x: X): Y
}
当然,人们必须指定抽象的概念:(例如)
implicit object Int2IntNumericConversion extends NumericConversion[Int, Int] {
def convert(i: Int): Int = i
}
implicit object Double2DoubleNumericConversion extends NumericConversion[Double, Double] {
def convert(d: Double): Double = d
}
implicit object Int2DoubleNumericConversion extends NumericConversion[Int, Double] {
def convert(i: Int): Double = i.toDouble
}
现在比较方法去如下:
def compareTwoNumbers1[N1, N2, N3](n1: N1, n2: N2)
(implicit conv1: NumericConversion[N1, N3],
conv2: NumericConversion[N2, N3],
ord: Ordering[N3]): Int = {
ord compare (conv1 convert n1, conv2 convert n2)
}
用法:
compareTwoNumbers1[Int, Double, Double](3, 8D) // -1
什么是贝蒂,我们要明确指出的类型参数,所以我尝试:
def compareTwoNumbers2[N3] = new {
def apply[N1, N2](n1: N1, n2: N2)(implicit conv1: NumericConversion[N1, N3],
conv2: NumericConversion[N2, N3],
ord: Ordering[N3]): Int = {
ord compare (conv1 convert n1, conv2 convert n2)
}
}
这减少了一个类型参数:
compareTwoNumbers2[Double](3, 8D) // -1
不能让人满意,所以我尝试这样做:
trait NumericUpperBound[Num1, Num2, UpperBound]
implicit object NumericUpperBoundIDD extends NumericUpperBound[Int, Double, Double]
implicit object NumericUpperBoundDID extends NumericUpperBound[Double, Int, Double]
随着新的比较方法:
def compareTwoNumbers3[N1, N2, N3](n1: N1, n2: N2)
(implicit nub: NumericUpperBound[N1, N2, N3],
conv1: NumericConversion[N1, N3],
conv2: NumericConversion[N2, N3],
ord: Ordering[N3]): Int = {
ord compare (conv1 convert n1, conv2 convert n2)
}
现在,它的工作原理:
compareTwoNumbers3(3, 8D) // -1
当然,必须创建所有原始类型的类。 但it's灵活的把它扩大到BigInt
,等以后。
编辑
通过@wvxvw其中提到的一个矩阵的评论NumericUpperBounds
启发了我绕过一个矩阵,这里是(不包括正在运行的例子Byte
和Short
的时刻):
trait ==>[X, Y] extends (X => Y)
object ==> {
def apply[X, Y](f: X => Y): X ==> Y = {
new (X ==> Y) {
def apply(x: X): Y = f(x)
}
}
}
implicit val Int2LongNumericConversion = ==> { x: Int => x.toLong }
implicit val Int2FloatNumericConversion = ==> { x: Int => x.toFloat }
implicit val Int2DoubleNumericConversion = ==> { x: Int => x.toDouble }
implicit val Long2FloatNumericConversion = ==> { x: Long => x.toFloat }
implicit val Long2DoubleNumericConversion = ==> { x: Long => x.toDouble }
implicit val Float2DoubleNumericConversion = ==> { x: Float => x.toDouble }
implicit def reflexiveNumericConversion[X]: X ==> X = new (X ==> X) { def apply(x: X): X = x }
trait NumericUpperBound[Num1, Num2, UpperBound]
implicit def reflexiveNumericUpperBound[X]: NumericUpperBound[X, X, X] = new NumericUpperBound[X, X, X] {}
implicit def inductiveNumericUpperBound1[X, Y](implicit ev: X ==> Y): NumericUpperBound[Y, X, Y] = new NumericUpperBound[Y, X, Y] {}
implicit def inductiveNumericUpperBound2[X, Y](implicit ev: X ==> Y): NumericUpperBound[X, Y, Y] = new NumericUpperBound[X, Y, Y] {}
def compareTwoNumbers[N1, N2, N3](n1: N1, n2: N2)
(implicit nub: NumericUpperBound[N1, N2, N3],
conv1: N1 ==> N3,
conv2: N2 ==> N3,
ord: Ordering[N3]): Int = {
ord compare (n1, n2)
}
compareTwoNumbers(9L, 13) // -1
只需使用排序类型类:
def cmp[A](arr: Array[A], number: A)(implicit ord: Ordering[A]) =
ord.gt(arr(0), number)
// or
def cmp[A](arr: Array[A], number: A)(implicit ord: Ordering[A]) = {
import ord._
arr(0) > number
}
scala> cmp(Array(4), 2)
res9: Boolean = true
scala> cmp(Array(BigInt(4)), BigInt(2))
res15: Boolean = true
scala> cmp(Array(4), 2.0)
<console>:9: error: type mismatch;
found : Array[Int]
required: Array[AnyVal]
scala> cmp(Array(4.0), 2)
<console>:9: error: type mismatch;
found : Array[Double]
required: Array[AnyVal]
目前,我正在学习Scala所以不要把我的回答太当回事,但我认为查看边界或类型约束可以帮助你。 结合的景观让你因为它们之间存在着隐含的观点,但它不会走得更远因为从BigInt有到无的BigDecimal隐观比较任意的标准号。
def compareTwoNumbers[A <% Double, B <% Double](array:Array[A], number:B) = array(0) > number
scala> compareTwoNumbers(Array(1, 2, 0), 0.99)
res1: Boolean = true
scala> compareTwoNumbers(Array(1.0, 2, 0), 0.99)
res2: Boolean = true
scala> compareTwoNumbers(Array(1.0, 2, 0), 1)
res3: Boolean = false
我很想看看BigInt有和BigDecimal的,可以支持并知道什么是我的解决方案的缺点。
这里是:
def cmp[T1, T2](arr: Array[T1], num: T2)
(implicit v12: T1 => T2 = null, v21: T2 => T1 = null,
ord1: Ordering[T1], ord2: Ordering[T2]): Boolean = v12 match {
case null => ord1.gt(arr(0), num)
case _ => ord2.gt(arr(0), num)
}
一些使用案例:
scala> cmp(Array(1,2), 0.1) //T1 = Int, T2 = Double
res3: Boolean = true
scala> cmp(Array(1.2, 2.3), 1) //T1 = Double, T2 = Int
res4: Boolean = true
scala> cmp(Array(1,2), BigInt(100)) //T1 = Int, T2 = BigInt
res5: Boolean = false
scala> cmp(Array(123.5 ,2233.9), BigDecimal(100)) //T1 = Double, T2 = BigDecimal
res6: Boolean = true
scala> cmp(Array(123.5 ,2233.9), 200.toByte)
res7: Boolean = true
def compareTwoNumbers(a: Number, b: Number) = {
a.floatValue() > b.floatValue() // this has to compile
}
它不是很难采用这样的代码:
def gt[A : Numeric, B: Numeric](first: A, second: B) = {
val a = implicitly[Numeric[A]].toDouble(first)
val b = implicitly[Numeric[B]].toDouble(second)
a > b
}
应该尝试不同类型的数字的工作:
scala> gt(4, 4.0)
res3: Boolean = false
scala> gt(4, 3.0)
res4: Boolean = true
scala> gt(3L, 4.0)
res5: Boolean = false