2 votes

Comment améliorer ce code scala qui vérifie si deux tableaux de bytes sont identiques?

J'ai une méthode pour comparer deux tableaux d'octets. Le code est en java-style, et il y a beaucoup de "if-else".

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 est nul alors que b2 ne l'est pas, et vice versa")
  }
}

J'ai essayé comme suit, mais cela n'a pas beaucoup simplifié le code :

(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 est nul alors que b2 ne l'est pas, et vice versa")
}

12voto

Matthew Farwell Points 31257

À moins que vous ne le fassiez dans le cadre d'un exercice académique, que diriez-vous de

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

La description :

Renvoie true si les deux tableaux spécifiés de bytes sont égaux l'un à l'autre. Deux tableaux sont considérés comme égaux si les deux contiennent le même nombre d'éléments, et que toutes les paires correspondantes d'éléments dans les deux tableaux sont égales. En d'autres termes, deux tableaux sont égaux s'ils contiennent les mêmes éléments dans le même ordre. De plus, deux références de tableau sont considérées comme égales si les deux sont nulles.

J'avoue que c'est du 'style java' :-)

Comme vous lancez des AssertionErrors, vous pouvez supprimer tous les else's :

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

  if (b1 == null || b2 == null) throw new AssertionError("b1 est null alors que b2 ne l'est pas, et 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))
  }
}

Si, comme je le soupçonne, vous utilisez cela dans des tests JUnit (d'où le assertArray), alors vous pouvez utiliser une astuce que j'utilise souvent, comparer les représentations en chaîne des tableaux:

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

ce qui vous donnera le même résultat (une AssertionError), avec où sont les différences.

6voto

Paul Butcher Points 5349

La bibliothèque standard fournit sameElements exactement dans ce but :

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

1voto

tenshi Points 13370

Une simplification possible :

def assertArray(b1: Array[Byte], b2: Array[Byte]) {
    (Option(b1), Option(b2)) match {
        case (None, _) => 
            throw new AssertionError("b1 est nul")
        case (_, None) => 
            throw new AssertionError("b2 est nul")
        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("Les tableaux ne correspondent pas")
        case _ => // tout va bien
    }
}

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

Probablement peut être encore amélioré, mais au moins il ne contient pas de if imbriqués et de boucles externes.

1voto

user unknown Points 15555

Une amélioration mineure de la solution de Matthew pourrait consister à renvoyer toutes les différences et non pas seulement la première :

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 est nul alors que b2 ne l'est pas, 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))
}

Invocation du test :

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)

1voto

爱国者 Points 3778

Voici ma solution utilisant la récursivité terminale:

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

et les cas de test

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

Que pensez-vous de ce lien https://gist.github.com/1322299

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X