如何用自己压缩一个IEnumerable

wag*_*ghe 7 .net c# linq algorithm geometry

我正在实现一些基于点列表的数学算法,如距离,面积,质心等.就像在这篇文章中一样:找到使用linq导航点列表所需的距离

该职位介绍如何通过基本上压缩和解序列"与自身",由1偏移的原始的IEnumerable的开始位置生成用于拉链的序列来计算点的序列(按顺序取)的总距离.

因此,考虑到在.NET 4.0中的zip扩展,假设点的点类型,以及合理的距离公式,你可以拨打电话这样从一个点到下一个,然后生成距离的顺序来总结的距离:

var distances = points.Zip(points.Skip(1),Distance);
double totalDistance = distances.Sum();
Run Code Online (Sandbox Code Playgroud)

面积和质心计算类似,因为它们需要迭代序列,处理每对点(点[i]和点[i + 1]).我想过制作一个通用的IEnumerable扩展,适合实现这些(可能还有其他)算法,这些算法在序列上运行,一次取两个项目(points [0]和points [1],points [1]和points [2], ...,点[n-1]和points [n](或者是n-2和n-1 ......)并应用函数.

我的通用迭代器将具有与Zip类似的签名,但它不会收到第二个要压缩的序列,因为它实际上只是自己压缩.

我的第一次尝试看起来像这样:

public static IEnumerable<TResult> ZipMyself<TSequence, TResult>(this IEnumerable<TSequence> seq, Func<TSequence, TSequence, TResult> resultSelector)
{
  return seq.Zip(seq.Skip(1),resultSelector);
}
Run Code Online (Sandbox Code Playgroud)

开始编辑: 在看到响应后,我已经实现了Pairwise,并明确使用了底层的Enumerator,如下所示:

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

    while (e.MoveNext()) yield return resultSelector(prev, prev = e.Current);
  }
}
Run Code Online (Sandbox Code Playgroud)

虽然肯定比我的初始版本更复杂,但是这一次迭代输入序列一次而原始迭代两次.

结束编辑

使用我的通用迭代器,我可以编写如下函数:

public static double Length(this IEnumerable<Point> points)
{
  return points.ZipMyself(Distance).Sum();
}
Run Code Online (Sandbox Code Playgroud)

并称之为:

double d = points.Length();
Run Code Online (Sandbox Code Playgroud)

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

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

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

public static bool IsClockwise(this IEnumerable<Point> points)
{
  return SignedArea(points) < 0;
}
Run Code Online (Sandbox Code Playgroud)

并称他们为:

double a = points.Area();
bool isClockwise = points.IsClockwise();
Run Code Online (Sandbox Code Playgroud)

在这种情况下,有没有理由不在Zip和Skip(1)方面实现"ZipMyself"?LINQ中是否已经存在自动化的东西(用自己压缩列表) - 而不是它需要更容易制作;-)

此外,扩展名是否有更好的名称可能反映出它是一个众所周知的模式(如果它确实是一个众所周知的模式)?

这里有关于面积计算的StackOverflow问题的链接.这是问题2432428.

还有关于Centroid的维基百科文章的链接.如果有兴趣,只需去维基百科并搜索Centroid.

刚刚开始,所以没有足够的代表发布多个链接.

开始编辑

为了完整性,如果有人在搜索距离,区域或质心后到达此处,这里是我的函数,它接受位置类型列表(假设为区域和质心关闭)并返回距离(沿),区域和质心.当前位置:

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<Position> pts)
  {
    return pts.Pairwise((p1, p2) => Position.Distance(p1, p2)).Sum();
  }

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

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

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

  public static Position Centroid(IEnumerable<Position> 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

随意评论.

结束编辑

Mar*_*ers 7

此外,扩展名是否有更好的名称可能反映出它是一个众所周知的模式(如果它确实是一个众所周知的模式)?

是的 - 它也被称为Pairwise.之前已经完成过,例如这里.还有之前曾去过一个关于它的问题在这里SO.

正如你所指出的,现在可以用Zip for .NET 4.0实现Pairwise.对于LINQ to Objects解决方案来说,这似乎是一种合理的方法,尽管在.NET v3.5上运行的版本也可能对更广泛的用户更有用.