(Число, Число) соответствует (Float, Int), но не соответствует (Int, Float) - PullRequest
3 голосов
/ 12 октября 2010

Это ошибка в Scala 2.8.0?(то же самое происходит с 2.8.1.RC2)

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
  }
}

Ответы [ 2 ]

3 голосов
/ 13 октября 2010

Я думаю, что это ошибка. Если вы посмотрите на сгенерированный байт-код:


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 проверку номера, выполняется переход к строке 45, которая выдает исключение. Это не кажется правильным. Я быстро просмотрел trac, но не смог напрямую найти проблему, касающуюся этого , поэтому может быть целесообразно создать один .

Редактировать Как указал Extempore, это ошибка, которая уже обнаружена, см. Его комментарий ниже.

2 голосов
/ 12 октября 2010

Я не знаю ответа на конкретный вопрос, но вот другой способ определения сравнения:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...