2010-10-12 89 views
3

这是Scala 2.8.0中的错误吗? (与2.8.1.RC2同样的情况)(数字,数字)匹配(浮点数,整数)但不匹配(Int,浮点数)

 
import junit.framework._ 
import Assert._ 

class BugTest extends TestCase { 

    def compare(first: Any, second: Any): Int = { 
     (first, second) match { 
     case (k: Int, o: Int) => k compare o 
     //why the next case matches (Float, Int) but does not match (Int, Float) ??? 
     case (k: Number, o: Number) => k.doubleValue() compare o.doubleValue() 
     case _ => throw new Exception("Unsupported compare " + 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))//this fails with an exception 
    } 
} 

回答

3

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


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检查开始。如果第一个变量是一个Integer,那么我们继续对第二个变量进行同样的检查。但是,如果第二次检查不成功,而不是再次在第81行继续使用数字检查,则会跳转到第45行,这会引发异常。这似乎不正确。我很快通过trac浏览,但无法直接找到有关此 的问题,因此创建一个 可能是明智之举。

编辑 正如Extempore指出的那样,这是一个已经存在于trac中的bug,请参阅他的评论。

+0

这是#1697 /#2337的一种表现形式。请参阅http://lampsvn.epfl.ch/trac/scala/ticket/2337。 – extempore 2010-10-13 06:46:40

+0

啊,很高兴知道,谢谢! – 2010-10-13 06:50:26

+0

谢谢。我已经添加了这个作为票的另一个例子。 – Andrey 2010-10-13 08:34:56

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 
+0

确保比较(1,1)总是返回0吗? – Andrey 2010-10-12 15:45:34

+0

对于Ints,是的。对于双打,它可以为您提供与比较任何双倍数值相同的保证。 – IttayD 2010-10-12 15:53:46

+0

谢谢你的回复。不幸的是,我的用例更大,我无法直接应用您的解决方案。 (我需要实现Erlang - http://www.erlang.org/doc/reference_manual/expressions.html#id72588) – Andrey 2010-10-13 08:24:53