Java Lambda - лучший способ найти экстремальные значения треугольника, содержащего экземпляры Vector3D - PullRequest
0 голосов
/ 02 июня 2018

Многоугольник треугольника имеет массив vertices, содержащий 3x Vector3D экземпляров.Метод должен найти крайнее значение (min / max) на оси (x, y или z) для треугольника.

Например, треугольник A(0, 0, 3) B(1, 2, 3) C(2, 2, -4) имеет минимальное значение -4 на оси z.

Текущая реализация выглядит следующим образом:

   public class Triangle implements Iterable<Vector3D> {
        private final Vector3D[] vertices;
        private final double kEpsilon = 1e-8;

        public Triangle(Vector3D... vertices) {
            Preconditions.checkArgument(vertices.length == 3);
            this.vertices = vertices;
        }

        public double getExtremeValueForAxis(int axis) {
            var minimum = getValueByAxisIndex(Arrays.stream(vertices).min((v, vOther) -> {
                var vValueOnAxis = getValueByAxisIndex(v, axis);
                var vOtherValueOnAxis = getValueByAxisIndex(vOther, axis);
                return Double.compare(vValueOnAxis, vOtherValueOnAxis);
            }).get(), axis);
            return minimum;
        }

        private static double getValueByAxisIndex(final Vector3D vertex, final int axis) {
            switch(axis) {
                case 0: return vertex.getX();
                case 1: return vertex.getY();
                case 2: return vertex.getZ();
                default: throw new IllegalArgumentException("Axis needs to be in 3D space!");
            }
        }
  }

Как разрешить пользователю искать минимальное / максимальное значение, передавая функцию Optional<T> min(Comparator<? super T> comparator); или Optional<T> max(Comparator<? super T> comparator);, чтобы избежать логического выраженияфлаг (например, boolean searchForMin) и как я могу уменьшить общую сложность моей реализации?

Ответы [ 2 ]

0 голосов
/ 02 июня 2018

Функция и ее вызовы действительно могут быть упрощены, если позволить вызывающей стороне выбрать как компаратор (в данном случае редуктор), так и ось, используя функции:

/** valueFunction selects the axis 
    reducer selects which of 2 values to retain (max/min in this case)*/
public double getExtremeValueForAxis(
   Function<Vector3D, Double> valueFunction, BinaryOperator<Double> reducer) {
     return Arrays.stream(vertices)
              .map(valueFunction)
              .reduce(reducer)
              .get();
}

Это можно вызвать так же просто, как:

new Triangle().getExtremeValueForAxis(Vector3D::getX, Math::max); //max by axis 0
new Triangle().getExtremeValueForAxis(Vector3D::getY, Math::min); //min by axis 1

Конечно, встроенные редукторы и преобразователи также могут быть предоставлены с использованием лямбда-выражений:

new Triangle().getExtremeValueForAxis(v -> v.getY(), 
            (n1, n2) -> n1 > n2 ? n2 : n1); //min by axis 0
0 голосов
/ 02 июня 2018

Я хотел бы отобразить элементы в потоке на int сначала с mapToInt(ToIntFunction<? super T> mapper), а затем использовать метод min(), чтобы получить минимальное значение.Этот метод не требует параметров, так как он работает вместо IntStream, и целые числа сравниваются напрямую.

int minX = Arrays.stream(vertices).mapToInt(Vector3D::getX).min().getAsInt();
int minY = Arrays.stream(vertices).mapToInt(Vector3D::getY).min().getAsInt();
int minZ = Arrays.stream(vertices).mapToInt(Vector3D::getZ).min().getAsInt();

int min =  Arrays.asList(minX, minY, minZ).stream().mapToInt(i -> i).min().getAsInt();

В качестве альтернативы, объедините все значения в один список и выполните над ним операции Stream.:

List<Integer> integers = new ArrayList<>();
Arrays.stream(vertices).forEach(i -> { 
    integers.add(i.getX()); integers.add(i.getY()); integers.add(i.getZ());
});
int min = integers.stream().mapToInt(i -> i).min().getAsInt();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...