Scala дженерики: числовые - PullRequest
       21

Scala дженерики: числовые

0 голосов
/ 05 декабря 2018

У меня есть следующий код Java:

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

public class NumTest {

    public static void main(String[] args) {
        final List<Integer> list1 = Arrays.asList(1, 2);
        final List<Float> list2 = Arrays.asList(3.0f, 4.0f);
        final List<Double> list3 = Arrays.asList(5.0, 6.0);
        assertCloseEnough(list1, Arrays.asList(1.0, 2.0));
        assertCloseEnough(list2, Arrays.asList(3.0, 4.0));
        assertCloseEnough(list3, Arrays.asList(5.0, 6.0));
    }

    private static void assertCloseEnough(List<? extends Number> actuals, List<? extends Number> expecteds) {
        assert actuals.size() == expecteds.size();
        for(int i = 0; i < actuals.size(); i++) {
            System.err.println(actuals.get(i).doubleValue());
            assert Math.abs(actuals.get(i).doubleValue() - expecteds.get(i).doubleValue()) < 1E-10;
        }
    }
}

Это работает, как и ожидалось, как вы можете проверить с помощью javac NumTest.java && java NumTest.

Мой вопрос: как мне написать эквивалент вScala?

Самый простой подход:

import Numeric.Implicits._

object TestNum extends App {

  assertCloseEnough(Seq(1,2), Seq(1.0, 2.0))
  assertCloseEnough(Seq(3.0f,4.0f), Seq(3.0, 4.0))
  assertCloseEnough(Seq(5.0,6.0), Seq(5.0, 6.0))

  def assertCloseEnough[N: Numeric](actuals: Seq[N], expecteds: Seq[N]): Unit = {
    assert(actuals.size == expecteds.size)
    val ad = actuals.map(implicitly[Numeric[N]].toDouble(_))
    val ed = expecteds.map(implicitly[Numeric[N]].toDouble(_))
    for (i <- expecteds.indices) {
      assert(Math.abs(ad(i) - ed(i)) < 1E-10)
    }
  }
}

Не работает:

TestNum1.scala:5: error: could not find implicit value for evidence parameter of type Numeric[AnyVal]
  assertCloseEnough(Seq(1,2), Seq(1.0, 2.0))
                   ^

Чуть более продвинутая версия:

import Numeric.Implicits._

object TestNum extends App {

  assertCloseEnough(Seq[Int](1,2), Seq[Double](1.0, 2.0))
  assertCloseEnough(Seq[Float](3.0f,4.0f), Seq[Double](3.0, 4.0))
  assertCloseEnough(Seq[Double](5.0,6.0), Seq[Double](5.0, 6.0))

  def assertCloseEnough[N: Numeric](actuals: Seq[N], expecteds: Seq[N]): Unit = {
    assert(actuals.size == expecteds.size)
    val ad = actuals.map(implicitly[Numeric[N]].toDouble(_))
    val ed = expecteds.map(implicitly[Numeric[N]].toDouble(_))
    for (i <- expecteds.indices) {
      assert(Math.abs(ad(i) - ed(i)) < 1E-10)
    }
  }
}

Также не работает, с той же ошибкой.

Глядя на другие вопросы здесь, такие как Универсальные Scala и Числовые последствия Я пришел к следующему:

import Numeric.Implicits._

object TestNum extends App {

  assertCloseEnough(Seq(1,2), Seq(1.0, 2.0))
  assertCloseEnough(Seq(3.0f,4.0f), Seq(3.0, 4.0))
  assertCloseEnough(Seq(5.0,6.0), Seq(5.0, 6.0))

  def assertCloseEnough[N: Numeric, T1 <% N, T2 <% N](actuals: Seq[T1], expecteds: Seq[T2]): Unit = {
    assert(actuals.size == expecteds.size)
    val ad = actuals.map(implicitly[Numeric[T1]].toDouble(_))
    val ed = expecteds.map(implicitly[Numeric[T2]].toDouble(_))
    for (i <- expecteds.indices) {
      assert(Math.abs(ad(i) - ed(i)) < 1E-10)
    }
  }
}

Что тоже не работает:

TestNum3.scala:5: error: ambiguous implicit values:
 both object BigIntIsIntegral in object Numeric of type scala.math.Numeric.BigIntIsIntegral.type
 and object IntIsIntegral in object Numeric of type scala.math.Numeric.IntIsIntegral.type
 match expected type Numeric[N]
  assertCloseEnough(Seq(1,2), Seq(1.0, 2.0))
                   ^

Что мне здесь не хватает?Как я могу заставить это работать?

Ответы [ 2 ]

0 голосов
/ 05 декабря 2018

Ваши последовательности имеют элементы двух разных типов, но вы пытаетесь параметризовать их как один.Примерно так должно работать:

 def assertCloseEnough[N1, N2](expected: Seq[N1], actual: Seq[N2])(implicit e1: Numeric[N1], e2: Numeric[N2]) {
    assert(
      expected.size == actual.size &&
      (expected zip actual).forall { case (a,b) => 
         math.abs(e1.toDouble(a)-e2.toDouble(b)) < 1e-10
      }
   )
 }

Эта декларация эквивалентна closeEnough[N1 : Numeric, N2 : Numeric]( ...), но в этом случае она немного удобнее, потому что она дает фактические имена для следствий "доказательств", так что вы не можетене нужно ловить их, используя implicitly[Numeric[N1]] ...

Кроме того, не используйте foo(i) с Seq, это почти всегда плохая идея.Если вы уверены, что вам нужен произвольный доступ (в большинстве случаев это не так), используйте IndexedSeq.

0 голосов
/ 05 декабря 2018

Вам необходимо использовать два типа (например, T1 и T2) и предоставить неявные аргументы для вашего метода или вызвать Numeric из неявной области с помощью implicitly

Два способа сделать это ниже:

def assertCloseEnough[T1: Numeric, T2: Numeric](actuals: Seq[T1], expecteds: Seq[T2]): Unit = {
  assert(actuals.size == expecteds.size)
  val ad = actuals.map(implicitly[Numeric[T1]].toDouble)
  val ed = expecteds.map(implicitly[Numeric[T2]].toDouble)
  for (i <- expecteds.indices) {
    assert(Math.abs(ad(i) - ed(i)) < 1E-10)
  }
}

def assertCloseEnough[T1, T2](actuals: Seq[T1], expecteds: Seq[T2])(implicit t1: Numeric[T1], t2: Numeric[T2]): Unit = {
  assert(actuals.size == expecteds.size)
  val ad = actuals.map(_.toDouble)
  val ed = expecteds.map(_.toDouble)
  for (i <- expecteds.indices) {
    assert(Math.abs(ad(i) - ed(i)) < 1E-10)
  }
}
...