Csharp/C#教程:如何用自己压缩一个IEnumerable分享


如何用自己压缩一个IEnumerable

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

该文章描述了如何通过基本上用“自身”压缩序列来计算一系列点的总距离(按顺序排列),通过将原始IEnumerable的起始位置偏移1来生成Zip的序列。

因此,给定.Net 4.0中的Zip扩展,假设点类型为Point,以及合理的距离公式,您可以进行这样的调用,以生成从一个点到下一个点的距离序列,然后对距离求和:

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

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

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

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

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

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

 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(); 

在这种情况下,是否有任何理由不在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 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) * cx, 1.0 / (a * 6.0) * cy); } } 

随意评论。

结束编辑

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

是的 – 它也被称为Pairwise 。 之前已经完成过,例如这里 。 在此之前还有一个关于它的问题。

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

当我做了类似的事情时,我称之为SelectWithPrevious并且有一个版本对“SelectWithPreviousItem”(采用Func )和“SelectWithPreviousResult”进行了重载(采用了Func ) 。

另外,我通过直接存储最后一个元素而不是像Zip方法那样迭代序列两次来实现它。 从来没有使用LINQ-to-SQL,我不能肯定地说,但我想知道Zip / Skip方法是否两次访问服务器以评估查询两次。

上述就是C#学习教程:如何用自己压缩一个IEnumerable分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/cdevelopment/1250564.html

(0)
上一篇 2022年11月4日
下一篇 2022年11月4日

精彩推荐