Как Архивировать один IEnumerable с собой

Я реализую некоторые математические алгоритмы на основе списков точек, как Расстояние, область, Центроид, и т.д. Точно так же, как в этом сообщении: Найдите расстояние требуемым переместиться по списку точек с помощью linq

То сообщение описывает, как вычислить общее расстояние последовательности точек (взятый в порядке) путем важного архивирования последовательности "с собой", генерации последовательности для Zip путем возмещения положения запуска исходного IEnumerable 1.

Так, учитывая расширение Zip в.Net 4.0, принимая Точку для типа точки и разумную формулу Расстояния, можно выполнить вызовы как это, чтобы генерировать последовательность расстояний от одной точки до следующего и затем суммировать расстояния:

var distances = points.Zip(points.Skip(1),Distance);
double totalDistance = distances.Sum();

Область и Центроидные вычисления подобны в этом, они должны выполнить итерации по последовательности, обработав каждую пару точек (точки [я] и точки [i+1]). Я думал о создании универсального расширения IEnumerable, подходящего для реализации их (и возможно другой) алгоритмы, которые работают по последовательностям, беря два объекта за один раз (точки [0] и точки [1], точки [1] и точки [2]..., точки [n-1] и точки [n] (или это n-2 и n-1...), и применение функции.

Мой универсальный итератор имел бы подобную подпись для Архивирования, но он не получит вторую последовательность для архивирования с тем, поскольку он действительно просто собирается архивировать с собой.

Моя первая попытка похожа на это:

public static IEnumerable ZipMyself(this IEnumerable seq, Func resultSelector)
{
  return seq.Zip(seq.Skip(1),resultSelector);
}

Начните редактирование: После наблюдения ответов я реализовал Попарно с конкретным видом использования базового Перечислителя как это:

public static IEnumerable Pairwise(this IEnumerable seq, Func resultSelector)
{
  TSequence prev = default(TSequence);
  using (IEnumerator e = seq.GetEnumerator())
  {
    if (e.MoveNext()) prev = e.Current;

    while (e.MoveNext()) yield return resultSelector(prev, prev = e.Current);
  }
}

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

Редактирование конца

С моим универсальным итератором на месте, я могу записать функции как это:

public static double Length(this IEnumerable points)
{
  return points.ZipMyself(Distance).Sum();
}

и назовите его как это:

double d = points.Length();

и

double GreensTheorem(Point p1, Point p1)
{
  return p1.X * p2.Y - p1.Y * p2.X;
}

public static double SignedArea(this IEnumerable points)
{
  return points.ZipMyself(GreensTheorem).Sum() / 2.0
}

public static double Area(this IEnumerable points)
{
  return Math.Abs(points.SignedArea());
}

public static bool IsClockwise(this IEnumerable points)
{
  return SignedArea(points) < 0;
}

и назовите их как это:

double a = points.Area();
bool isClockwise = points.IsClockwise();

В этом случае разве там какая-либо причина НЕ состоит в том, чтобы реализовать "ZipMyself" с точки зрения Zip и Пропуска (1)? Уже есть ли что-то в LINQ, который автоматизирует это (архивирование списка с собой) - не, что это должно быть сделано этим намного легче ;-)

Кроме того, есть ли лучшее название расширения, которое могло бы отразить, что это - известный шаблон (если, действительно это - известный шаблон)?

Имел ссылку здесь для вопроса о StackOverflow о вычислении области. Это - вопрос 2432428.

Также имел ссылку на статью Wikipedia о Центроиде. Просто перейдите к Википедии и поиску Центроида, если заинтересовано.

Только начав, не имейте достаточного количества представителя для регистрации больше чем одной ссылки.

Начните редактирование

Для полноты, если кто-либо добирается здесь после поиска Расстояния, области или Центроида, вот мои функции, которые принимают список типов Положения (принятый закрытый для области и Центроида) и возвращают Расстояние (вперед), область и Центроид Положений:

public struct Position
{
  public double X;
  public double Y;

  static public double Distance(Position p1, Position p2)
  {
    double dx = p2.X - p1.X;
    double dy = p2.Y - p1.Y;
    return Math.Sqrt(dx*dx + dy*dy);
  }
}

public static class PointMath
{
  public static double Distance(IEnumerable pts)
  {
    return pts.Pairwise((p1, p2) => Position.Distance(p1, p2)).Sum();
  }

  private static bool IsClockwise(IEnumerable pts)
  {
    return SignedArea(pts) < 0;
  }

  private static double SignedArea(IEnumerable pts)
  {
    return pts.Pairwise((p1, p2) => (p1.X * p2.Y - p1.Y * p2.X)).Sum() / 2.0;
  }

  public static double Area(IEnumerable pts)
  {
    return Math.Abs(SignedArea(pts));
  }

  public static Position Centroid(IEnumerable pts)
  {
    double a = SignedArea(pts);

    var  c = pts.Pairwise((p1, p2) => new 
                                      { 
                                        x = (p1.X + p2.X) * (p1.X * p2.Y - p2.X * p1.Y), 
                                        y = (p1.Y + p2.Y) * (p1.X * p2.Y - p2.X * p1.Y)   
                                      })
                .Aggregate((t1, t2) => new 
                                       { 
                                         x = t1.x + t2.x, 
                                         y = t1.y + t2.y 
                                       });

    return new Position(1.0 / (a * 6.0) * c.x, 1.0 / (a * 6.0) * c.y);
  }
}

Не стесняйтесь комментировать.

Редактирование конца

7
задан Community 23 May 2017 в 12:13
поделиться

2 ответа

Также, есть ли лучшее название для расширения, которое могло бы отразить, что это хорошо известный паттерн (если, действительно, это хорошо известный паттерн)?

Да - он также известен как Pairwise. Это делалось и раньше, например здесь. Также был вопрос об этом раньше здесь на SO.

Теперь, как вы указываете, парный метод может быть реализован в терминах Zip для .NET 4.0. Это кажется разумным подходом для решения LINQ to Objects, хотя наличие версии, работающей и на .NET v3.5, вероятно, более полезно для более широкой аудитории на данный момент.

7
ответ дан 7 December 2019 в 01:18
поделиться

Когда я сделал что-то подобное, я он назывался SelectWithPrevious и имел версию с перегрузками для SelectWithPreviousItem (использовала Func ) и «SelectWithPreviousResult» (использовала Func ).

Кроме того, я реализовал это, напрямую сохранив последний элемент, а не повторяя последовательность дважды, как это делает подход Zip. Я никогда не использовал LINQ-to-SQL, я не могу сказать наверняка, но мне интересно, совершает ли подход Zip / Skip два обращения к серверу, чтобы дважды оценить запрос.

3
ответ дан 7 December 2019 в 01:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: