Рассчитайте ограничивающий прямоугольник из списка точек, используя эликсир - PullRequest
0 голосов
/ 01 марта 2019

С учетом списка точек

[
  %{ x: 3, y: 8 },
  ...,
  %{ x: 1, y: 4 }
]

Каков наилучший способ вычисления координат блока, который будет содержать все точки - т.е. границы: %{ x1: 1, y1: 4, x2: 3, y2: 8 }

У меня естьпредчувствие, что я могу использовать Enum.flat_map_reduce/3, но синтаксис меня сейчас озадачивает.

Ответы [ 4 ]

0 голосов
/ 02 марта 2019

Я действительно столкнулся с этим как часть «Адвент кода 2018», день 10 в прошлом году. Это был мой метод .Он устанавливает ограничивающий прямоугольник на начальную точку, а затем расширяет его, поскольку находит точки дальше.Нет необходимости в nil.: -)

Здесь он адаптирован под ваши данные:

# Start with the first point, then compare all the others to find the extremities
def bounding_box([%{x: x, y: y} | points]) do
  Enum.reduce(points, %{x1: x, y1: y, x2: x, y2: y}, fn point, box ->
    %{
      x1: min(point.x, box.x1),
      y1: min(point.y, box.y1),
      x2: max(point.x, box.x2),
      y2: max(point.y, box.y2)
    }
  end)
end
0 голосов
/ 01 марта 2019

Я бы предложил это как более читабельное, чем Enum.reduce:

input = [%{x: 3, y: 8}, %{x: 1, y: 4}]

%{
  x1: input |> Enum.map(& &1.x) |> Enum.min(),
  x2: input |> Enum.map(& &1.x) |> Enum.max(),
  y1: input |> Enum.map(& &1.y) |> Enum.min(),
  y2: input |> Enum.map(& &1.y) |> Enum.max()
}

Конечно, есть и обратная сторона многократного повторения списка.В зависимости от ваших требований, это может быть проблемой.

0 голосов
/ 02 марта 2019

Я думаю, что Enum.reduce/3 - верный путь, но Kernel.min/2 и Kernel.max/2 могут достаточно хорошо обрабатывать логику принятия решения (нам просто нужно увеличить max, чтобы отклонить nil)

defmodule BoundingBox do
  @moduledoc """
  Creates a bounding box around coordinates.
  """
  @initial %{x1: nil, y1: nil, x2: nil, y2: nil}

  def new(enumerable) do
    Enum.reduce(enumerable, @initial, &get_bounds/2)
  end

  defp get_bounds(%{x: x, y: y}, %{x1: left, y1: bottom, x2: right, y2: top}) do
    %{x1: min(left, x), y1: min(bottom, y), x2: max_num(right, x), y2: max_num(top, right)}
  end

  defp max_num(nil, b), do: b
  defp max_num(a, b), do: max(a, b)
end
0 голосов
/ 01 марта 2019

Enum.reduce/3 будет достаточно.

input = [%{x: 3, y: 8}, %{x: 1, y: 4}]

Enum.reduce(input, %{x1: nil, x2: nil, y1: nil, y2: nil}, fn
  %{x: x, y: y}, %{x1: x1, x2: x2, y1: y1, y2: y2} ->
    %{
      x1: if(x < x1, do: x, else: x1),
      x2: if(is_nil(x2) or x > x2, do: x, else: x2),
      y1: if(y < y1, do: y, else: y1),
      y2: if(is_nil(y2) or y > y2, do: y, else: y2),
    }
end)

Число в Erlang (и, следовательно, Elixir ) равно меньше , чем у любого другого типа, поэтому nils для x1 и y1 просто в порядке.Для x2 и y2 требуется дополнительное условие.

...