【问题标题】:(Number, Number) matches (Float, Int) but does not match (Int, Float)(Number, Number) 匹配 (Float, Int) 但不匹配 (Int, Float)
【发布时间】:2011-04-24 08:55:24
【问题描述】:

这是 Scala 2.8.0 中的错误吗? (同样的情况发生在 2.8.1.RC2)

导入junit.framework._ 导入断言._ 类 BugTest 扩展 TestCase { def compare(first: Any, second: Any): Int = { (第一,第二)匹配{ case (k: Int, o: Int) => k 比较 o //为什么下一个 case 匹配 (Float, Int) 但不匹配 (Int, Float) ??? case (k: Number, o: Number) => k.doubleValue() 比较 o.doubleValue() case _ => throw new Exception("不支持的比较 " + first + "; " + second) } } def testCompare() { assertEquals("Both Int", -1, compare(0, 1)) assertEquals("Both Float", 1, compare(1.0, 0.0)) assertEquals("Float then Int", 0, compare(10.0, 10)) assertEquals("Int then Float", 0, compare(10, 10.0))//这个失败并抛出异常 } }

【问题讨论】:

    标签: scala


    【解决方案1】:

    我认为这是一个错误。如果查看生成的字节码:

    
    public int compare(java.lang.Object, java.lang.Object);
      Code:
       Stack=4, Locals=7, Args_size=3
       0:   aload_1
       1:   astore_3
       2:   aload_2
       3:   astore  4
       5:   aload_3
       6:   instanceof  #8; //class java/lang/Integer
       9:   ifeq    81
       12:  aload_3
       13:  invokestatic    #14; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
       16:  istore  5
       18:  aload   4
       20:  instanceof  #8; //class java/lang/Integer
       23:  ifeq    45
       26:  getstatic   #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
       29:  iload   5
       31:  invokevirtual   #24; //Method scala/Predef$.intWrapper:(I)Lscala/runtime/RichInt;
       34:  aload   4
       36:  invokestatic    #14; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
       39:  invokevirtual   #29; //Method scala/runtime/RichInt.compare:(I)I
       42:  goto    124
       45:  new #31; //class java/lang/Exception
    
       //part omitted for brevity..
    
       81:  aload_3
       82:  instanceof  #54; //class java/lang/Number
       85:  ifeq    161
       88:  aload_3
       89:  checkcast   #54; //class java/lang/Number
       92:  astore  6
       94:  aload   4
       96:  instanceof  #54; //class java/lang/Number
       99:  ifeq    125
       102: getstatic   #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
       105: aload   6
       107: invokevirtual   #58; //Method java/lang/Number.doubleValue:()D
       110: invokevirtual   #62; //Method scala/Predef$.doubleWrapper:(D)Lscala/runtime/RichDouble;
       113: aload   4
       115: checkcast   #54; //class java/lang/Number
       118: invokevirtual   #58; //Method java/lang/Number.doubleValue:()D
    
    

    在第 6 行,检查第一个变量是否是 java.lang.Integer 的实例。如果这不成功,我们继续第 81 行,该行以 java.lang.Number 检查开始。如果第一个变量是整数,那么我们继续对第二个变量进行相同的检查。但是,如果第二次检查不成功,则不会在第 81 行继续进行 Number 检查,而是跳转到第 45 行,这会引发异常。这似乎不正确。我快速浏览了 trac,但无法直接找到有关此 的问题,因此创建一个 可能是明智的。

    编辑 正如 Extempore 所指出的,这是一个已经存在于 trac 中的错误,请参阅下面的评论。

    【讨论】:

    【解决方案2】:

    我不知道具体问题的答案,但这里有另一种定义比较的方法:

    def compare[A : Numeric, B: Numeric](first: A, second: B): Int =
      implicitly[Numeric[A]].toDouble(first) compare implicitly[Numeric[B]].toDouble(second)
    
    scala> compare(0.0,1.0)
    res30: Int = -1
    
    scala> compare(0.0,1)
    res31: Int = -1
    
    scala> compare(0,1.0)
    res32: Int = -1
    
    scala> compare(0,1)
    res33: Int = -1
    

    【讨论】:

    • 这能保证 compare(1, 1) 总是返回 0 吗?
    • 对于整数,是的。对于双精度数,它为您提供与比较任何双精度值相同的保证。
    • 感谢您的回复。不幸的是,我的用例要大得多,我无法直接应用您的解决方案。 (我需要实现 Erlang - erlang.org/doc/reference_manual/expressions.html#id72588
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-03
    • 1970-01-01
    • 2018-11-28
    • 2014-12-16
    • 1970-01-01
    • 2011-10-11
    相关资源
    最近更新 更多