Як покращити цей код скала, який перевіряє, чи два байта масиву однакові?

У мене є метод для порівняння двох масивів байтів. Код є java-стилем, і є багато "якщо-ще" s.

def assertArray(b1: Array[Byte], b2: Array[Byte]) {
  if (b1 == null && b2 == null) return;
  else if (b1 != null && b2 != null) {
    if (b1.length != b2.length) throw new AssertionError("b1.length != b2.length")
    else {
      for (i <- b1.indices) {
        if (b1(i) != b2(i)) throw new AssertionError("b1(%d) != b2(%d)".format(i, i))
      }
    }
  } else {
    throw new AssertionError("b1 is null while b2 is not, vice versa")
  }
}

Я намагався виконати наступні дії, але код не спрощений:

(Option(b1), Option(b2)) match {
    case (Some(b1), Some(b2)) => if ( b1.length == b2.length ) {
       for (i <- b1.indices) {
        if (b1(i) != b2(i)) throw new AssertionError("b1(%d) != b2(%d)".format(i, i))
       }
    } else {
       throw new AssertionError("b1.length != b2.length")
    }
    case (None, None) => _
    case _ => throw new AssertionError("b1 is null while b2 is not, vice versa")
}
2
Як це код Scala, чому ти позначив його [java]?
додано Автор Raedwald, джерело
Тому що це Java-стиль
додано Автор Freewind, джерело
Ви можете опублікувати це питання на codereview.stackexchange.com. Це чудово підходить для питань "як покращити свій код".
додано Автор barjak, джерело

5 Відповіді

Якщо ви не робите це як академічне вправу, як про це

java.util.Arrays.equals(b1, b2)

Опис:

Повертає істину, якщо два вказані масиви байтів дорівнюють одному   інший. Два масиву вважаються рівними, якщо обидва масиву містять   однакове число елементів, і всі відповідні пари елементів у   два масиву рівні. Іншими словами, два масиву рівні, якщо вони   містять ті самі елементи в тому ж порядку. Також два масиви   посилання вважаються рівними, якщо обидва є нульовими.

Я визнаю це "Java-стилем" :-)

Оскільки ви кидаєте AssertionErrors, ви можете видалити всі інші:

def assertArray(b1: Array[Byte], b2: Array[Byte]): Unit = {
  if (b1 == b2) return;

  if (b1 == null || b2 == null) throw new AssertionError("b1 is null while b2 is not, vice versa")  

  if (b1.length != b2.length) throw new AssertionError("b1.length != b2.length")

  for (i <- b1.indices) {
    if (b1(i) != b2(i)) throw new AssertionError("b1(%d) != b2(%d)".format(i, i))
  }
}

Якщо, як я підозрюю, ви насправді використовуєте це в тестах JUnit (отже, assertArray), то ви можете використовувати трюк, який я часто роблю, порівняти рядкові уявлення масивів:

def assertArray2(b1: Array[Byte], b2: Array[Byte]): Unit = {
  assertEquals(toString(b1), toString(b2))
}

def toString(b: Array[Byte]) = if (b == null) "null" else java.util.Arrays.asList(b:_*).toString

що дасть вам той самий результат (AssertionError), звідки виникають розбіжності.

12
додано
@Peter Я не думаю, що це буде працювати, оскільки == в Масивах є еталонною рівністю: див. Цей Q & A. stackoverflow.com/q/2481149/770361 Однак, з відповіді Рейкса, це звучить так, як ви могли явно загорнути масиви, щоб зробити це робота
додано Автор Luigi Plinge, джерело
+1: перше тестування можна спростити до if (b1 == b2) return;
додано Автор Peter Lawrey, джерело
== працює для масивів у тому випадку, якщо b1 і b2 обидва нульові, це буде вірно, а якщо b1 і b2 є одним і тим же об'єктом, то це буде вірно.
додано Автор Peter Lawrey, джерело
@MatthewFarwell, True, тому тільки if (b1 == null || b2 == null) можна замінити на if (b1 == b2)
додано Автор Peter Lawrey, джерело
@Peter Тільки для того, щоб бути ясним, масивів. Рівність повертає true, якщо елементи однакові, але об'єкти різні.
додано Автор Matthew Farwell, джерело
На жаль, я думав, що ви мали на увазі перший тест (java.util.Arrays.equals (b1, b2)), а не перший тест у методі. Ви, звичайно, маєте рацію :-)
додано Автор Matthew Farwell, джерело

Стандартна бібліотека забезпечує sameElements саме для цієї мети:

scala> val a1 = Array[Byte](1, 3, 5, 7); val a2 = Array[Byte](1, 3, 5, 7); val a3 = Array[Byte](1, 3, 5, 7, 9)
a1: Array[Byte] = Array(1, 3, 5, 7)
a2: Array[Byte] = Array(1, 3, 5, 7)
a3: Array[Byte] = Array(1, 3, 5, 7, 9)

scala> a1 sameElements a2
res0: Boolean = true

scala> a1 sameElements a3
res1: Boolean = false
6
додано
Ні, це не вдається, якщо один, a1 або a2 є нульовим.
додано Автор user unknown, джерело
Це працює, тоді як прості == або рівні не.
додано Автор Gavin, джерело

Ось моє рішення з використанням рекурсивного хреста:

@scala.annotation.tailrec
def assertArray[T](b1: Array[T], b2: Array[T])(implicit m: Manifest[T]) : Unit = (b1, b2)  match{
    case (null, null) => 
    case (null, a) if a != null => throw new AssertionError 
    case (a, null) if a != null => throw new AssertionError  
    case (Array(), Array()) => 
    case _  => if (b1.length != b2.length ||  b1.head != b2.head ) throw new AssertionError  else assertArray(b1.tail,b2.tail)  
}

і тестові шафи

assertArray(null,null)
assertArray(Array[Byte](),null)
assertArray(null,Array[Byte]())
assertArray(Array[Byte](),Array[Byte]())
assertArray(Array[Byte](),Array[Byte](1))
assertArray(Array[Byte](1,2,3),Array[Byte](1,2,3))
assertArray(Array[Byte](1,3),Array[Byte](1))

Як про це https://gist.github.com/1322299 посилання

1
додано

Незначне поліпшення рішення Меттья може бути, щоб повернути всі розбіжності, а не тільки перші:

def assertArray (b1: Array[Byte], b2: Array[Byte]): Unit = {

  def diffs [T] (a: Array[T], b: Array[T]) = 
    (a.zip (b).filter (e => (e._1 != e._2)))

  if (b1 == null && b2 == null) 
    return;
  if (b1 == null || b2 == null) 
    throw new AssertionError ("b1 is null while b2 is not, vice versa")  
  if (b1.length != b2.length) 
    throw new AssertionError ("b1.length != b2.length")
  val delta = diffs (b1, b2)
  delta.map (d => throw new AssertionError ("" + delta.mkString ))
}

Тестовий виклик:

val ab = (List ((List (47, 99, 13, 23, 42).map (_.toByte)).toArray,
  (List (47, 99, 33, 13, 42).map (_.toByte)).toArray)).toArray

assertArray (ab(0), ab(1))
// java.lang.AssertionError: (13,33)(23,13)
1
додано

Одне з можливих спрощень:

def assertArray(b1: Array[Byte], b2: Array[Byte]) {
    (Option(b1), Option(b2)) match {
        case (None, _) => 
            throw new AssertionError("b1 is null")
        case (_, None) => 
            throw new AssertionError("b2 is null")
        case (Some(Size(b1Size)), Some(Size(b2Size))) if b1Size != b2Size  => 
            throw new AssertionError("b1.length != b2.length")
        case (Some(b1), Some(b2)) if b1 zip b2 find (c => c._1 != c._2) isDefined => 
            throw new AssertionError("Arrays do not match")
        case _ =>//everything is OK
    }
}

object Size {
    def unapply[T](arr: Array[T]): Option[Int] = Some(arr.size)
}

Напевно, його можна покращити ще більше, але принаймні, він не має вкладеного if s та зовнішніх циклів.

1
додано
ІТ КПІ - Java
ІТ КПІ - Java
436 учасників

ІТ КПІ - Scala
ІТ КПІ - Scala
45 учасників