tags:

views:

77

answers:

2

Using linq given a IEnumerable of two dimensional points, Find the distance required to navigate all points assuming the points are visited in the order they appear in the list.

I thought I might be able to somehow use the .Aggregate<> function but can't figure out how to make it work. The problem is the aggregate returns the same type as the list, which is a point I need a scalar (double).

If this problem is not solvable using linq, it would be interesting to know what class of problem can and what class of problem can not be solved using linq.

This is not homework but is a required step in a personal project to help me learn linq.

+1  A: 

How about this? Assuming:

struct Point 
{
 public int X;
 public int Y;
 public Point(int x, int y) { X=x; Y=y; }
}

double Distance(Point p1, Point p2)
{
    return Math.Sqrt( Math.Pow(p1.X-p2.X,2) + Math.Pow(p1.Y-p2.Y,2) );
}

var list = new List<Point>{...};

Then

var distance = list
    .Select( (p,i) => i == list.Count-1? 0 : Distance(p,list[i+1]) ).Sum();
Winston Smith
The actual list is calculated as part of a bigger linq expression, so it is a IEnumerable<Point> , I cant index into it
trampster
You could always call `.ToList()` on the `IEnumerable<Point>`
Winston Smith
I would rather not force the execution until then end of the expression if possible
trampster
+4  A: 

This is much easier if you use (or replicate) the Zip extension method available in .net 4:

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

using the Distance method:

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

Bart explains (and shows you how to implement) zip on his blog:

static class Enumerable 
{ 
    public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> func) 
    { 
        var ie1 = first.GetEnumerator(); 
        var ie2 = second.GetEnumerator();

        while (ie1.MoveNext() && ie2.MoveNext()) 
            yield return func(ie1.Current, ie2.Current); 
    } 
}
Rob Fonseca-Ensor
actually this is exactly what I want, thanks
trampster