繁体   English   中英

LINQ最小/最大和最小化迭代

[英]LINQ Min/Max and minimizing iterations

我正在编写一个函数,该函数需要一系列System.Windows.Point并返回带有所有点的边界X和Y值的ValueTuple。 这旨在确定图形轴的标签。

我试图将执行的列表的迭代次数减至最少。 经过大量的谷歌搜索,我已经采用了一种类似下面的方法(被告知:“已复制”),我被告知可以做到这一点。 但是我不确定如何验证这一事实。 我想知道是否更熟悉LINQ的人可以

  1. 确认以下函数确实只对列表进行一次迭代,即使它正在计算4个不同的值
  2. 如果是这样,请向我解释这是怎么回事。 因为在我看来,正在构造的匿名类型在给定列表中分别两次调用“ Min”和“ Max”。 为什么没有四次迭代呢?
  3. 也许甚至可以解释一下我如何亲自验证发生的迭代次数,以便将来不再需要提出这样的问题。 我不知道该怎么做。

我的LINQ-Fu还不牢固。

谢谢

    /// <summary>
    /// X and Y axis boundaries in the form of a System.ValueTuple.
    /// </summary>
    public (double MinX, double MaxX, double MinY, double MaxY) 
    GetBounds(List<System.Windows.Point> pts)
    {


        // Calculate the bounds with a LINQ statement.  Is this one iteration or many?

        var a = pts.GroupBy(i => 1).Select(
            pp => new
            {
                MinY = pp.Min(p => p.Y),
                MaxY = pp.Max(p => p.Y),
                MinX = pp.Min(p => p.X),
                MaxX = pp.Max(p => p.X)
            }).FirstOrDefault();


        return a != null ? (a.MinX, a.MaxX, a.MinY, a.MaxY) : (0, 0, 0, 0);
    }

确认以下函数确实只对列表进行一次迭代,即使它正在计算4个不同的值

否-原始清单将有效地重复4次。 您正在创建将包装原始集合的“空”分组,以便可以将集合“投影”到单个对象。 由于您在“分组”上调用了4个linq函数-原始列表将被迭代4次。 它在功能上等同于:

var a = new
        {
            MinY = pts.Min(p => p.Y),
            MaxY = pts.Max(p => p.Y),
            MinX = pts.Min(p => p.X),
            MaxX = pts.Max(p => p.X)
        };

如果这对您来说是个问题 ,查找边界的惯用方式是使用foreach循环并手动跟踪最小和最大x和y坐标。 这将是一个相对简短的函数,并将迭代次数减少75%:

int MinX, MaxX, MinY, MaxY;
MaxX = MaxY = Int.MinValue;
MinX = MinY = Int.MaxValue;
foreach(Point p in pts)
{
    MinX = Math.Min(p.X, MinX);
    MaxX = Math.Max(p.X, MaxX);
    MinY = Math.Min(p.Y, MinY);
    MaxY = Math.Max(p.Y, MaxY);
}
var a = new
    {
        MinY,
        MaxY,
        MinX,
        MaxX
    };

可以使用Aggregate循环查找带有lambda的最小值和最大值:

var a = pts.Aggregate(
     new {
        MinX = int.MaxValue,
        MaxX = int.MinValue,
        MinY = int.MaxValue,
        MaxY = int.MinValue
    },
    (acc, p) => new {
        MinX = Math.Min(p.X, acc.MinX);
        MaxX = Math.Max(p.X, acc.MaxX);
        MinY = Math.Min(p.Y, acc.MinY);
        MaxY = Math.Max(p.Y, acc.MaxY);
    });

但是聚合器将为源集合中的每个对象创建一个对象,再为“初始”对象创建一个对象。 因此,列表只会被迭代一次,但是会创建多个临时对象,从而增加了需要进行GC处理的内存量。

您在此处使用的方法至少对输入值进行五次迭代(一次将它们“分组”,然后对每个最小值/最大值进行一次迭代),这是进行操作的一种极其奇怪的方法。

当您要收集一组值并将其压缩为一个值时,首选方法是.Aggregate (在其他语言中也称为reducefold )。

在您的情况下,您可以这样做。 它应该只对您的集合进行一次迭代:

public static (double minX, double maxX, double minY, double maxY) 
GetBounds(List<Point> pts)
{
    return pts.Aggregate(
        (Int32.MaxValue, Int32.MinValue, Integer.MaxValue, Int32.MinValue),
        (acc, point) => 
        (
            Math.Min(point.X, acc.Item1),
            Math.Max(point.X, acc.Item2),
            Math.Min(point.Y, acc.Item3),
            Math.Max(point.Y, acc.Item4)
        ));
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM