我實現基於點的名單一些數學算法,如距離,面積,重心等就像在這個帖子:Find the distance required to navigate a list of points using linq如何郵編一個IEnumerable的自身
該職位介紹如何計算總通過基本上將序列「自身」壓縮以產生Zip序列,通過將原始IEnumerable的開始位置偏移1來排列點序列(按順序排列)的距離。
因此,給定Zip擴展。 Net 4.0,假設點類型的點和合理的距離公式,您可以調用這樣的方式來生成從一點到下一點的距離序列,然後求和距離:
var distances = points.Zip(points.Skip(1),Distance);
double totalDistance = distances.Sum();
面積和質心計算是因爲它們需要遍歷序列,處理每對點相似(點[i]和點第[i + 1])。我想要製作一個通用的IEnumerable擴展,它適用於實現這些(也可能是其他)算法,這些算法在序列上運行,一次只處理兩個項目(點[0]和點[1],點[1]和點[2]), ...,點[n-1]和點[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);
}
開始編輯:通過輸入序列,而原來的迭代
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);
}
}
雖然比我最初的版本肯定更加複雜,這一個迭代一次: 看到的答覆後,我已經實現配對,像這樣明確使用底層枚舉兩次。
編輯完
與我的地方通用的迭代,我可以寫這樣的功能:
public static double Length(this IEnumerable<Point> 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<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;
}
,並呼籲他們這樣的:
double a = points.Area();
bool isClockwise = points.IsClockwise();
在這種情況下,沒有任何理由不以郵編方面實行「ZipMyself」和Skip(1)? 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);
}
}
隨時發表評論。
編輯完
我接受這個答案,主要是因爲對於術語Pairwise和鏈接到Pairwise引用。我在StackOverflow上看到了Pairwise鏈接,但在搜索這個問題時我沒有遇到它。我會在這裏注意到,正如我在回覆Gideon Engelberth時所做的那樣,實現上述方式確實會導致輸入IEnumerable被迭代兩次,根據IEnumerable或上游必須執行的操作,我認爲這可能會很昂貴生成答案。 – wageoghe 2010-05-05 19:44:49