为 linq groupby 编写自定义比较器 [英] writing a custom comparer for linq groupby

查看:26
本文介绍了为 linq groupby 编写自定义比较器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

同样,这个示例是我的实际问题的一个非常简化的版本,涉及用于 linq 分组的自定义比较器.我做错了什么?

Again this sample is a very simplified version of my actual problem involving a custom comparer for linq grouping. What have I done wrong?

下面的代码产生下面的结果(1.2, 0),(4.1, 0), (4.1, 0),(1.1, 0),

The code below produces the result below (1.2, 0), (4.1, 0), (4.1, 0), (1.1, 0),

但是我期待以下内容,因为 1.1 和 1.2

相差 1.0.(1.2, 0), (1.1, 0),(4.1, 0), (4.1, 0),

however I was expecting the following since 1.1 and 1.2 are < 1.0 apart. (1.2, 0), (1.1, 0), (4.1, 0), (4.1, 0),

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<Point> points = new List<Point> { 
            new Point(1.1, 0.0)
            , new Point(4.1, 0.0) 
            , new Point(1.2, 0.0)
            , new Point(4.1, 0.0)
        };

        foreach (var group in points.GroupBy(p => p, new PointComparer()))
        {
            foreach (var num in group)
                Console.Write(num.ToString() + ", ");

            Console.WriteLine();
        }

        Console.ReadLine();
    }
}

class PointComparer : IEqualityComparer<Point>
{
    public bool Equals(Point a, Point b)
    {
        return Math.Abs(a.X - b.X) < 1.0;
    }

    public int GetHashCode(Point point)
    {
        return point.X.GetHashCode()
            ^ point.Y.GetHashCode();
    }
}

class Point
{
    public double X;
    public double Y;

    public Point(double p1, double p2)
    {
        X = p1;
        Y = p2;
    }

    public override string ToString()
    {
        return "(" + X + ", " + Y + ")";
    }
}

推荐答案

使用相等比较器的分组算法(以及我认为所有的 LINQ 方法)总是首先比较哈希码,并且只有在两个时才执行 Equals哈希码是相等的.您可以看到,如果在相等比较器中添加跟踪语句:

The grouping algorithm (and I think all LINQ methods) using an equality comparer always first compares hash codes and only executes Equals if two hash codes are equal. You can see that if you add tracing statements in the equality comparer:

class PointComparer : IEqualityComparer<Point>
{
    public bool Equals(Point a, Point b)
    {
        Console.WriteLine("Equals: point {0} - point {1}", a, b);
        return Math.Abs(a.X - b.X) < 1.0;
    }

    public int GetHashCode(Point point)
    {
        Console.WriteLine("HashCode: {0}", point);
        return point.X.GetHashCode()
            ^ point.Y.GetHashCode();
    }
}

结果:

HashCode: (1.1, 0)
HashCode: (4.1, 0)
HashCode: (1.2, 0)
HashCode: (4.1, 0)
Equals: point (4.1, 0) - point (4.1, 0)
(1.1, 0), 
(4.1, 0), (4.1, 0), 
(1.2, 0), 

仅对哈希码相等的两个点执行Equals.

Only for the two points with equal hash codes Equals was executed.

现在你可以通过总是返回 0 作为哈希码来欺骗比较.如果你这样做,输出将是:

Now you could trick the comparison by always returning 0 as hash code. If you do that, the output will be:

HashCode: (1.1, 0)
HashCode: (4.1, 0)
Equals: point (1.1, 0) - point (4.1, 0)
HashCode: (1.2, 0)
Equals: point (4.1, 0) - point (1.2, 0)
Equals: point (1.1, 0) - point (1.2, 0)
HashCode: (4.1, 0)
Equals: point (4.1, 0) - point (4.1, 0)
(1.1, 0), (1.2, 0), 
(4.1, 0), (4.1, 0), 

现在对于每一对 Equals 都被执行了,你就得到了你的分组.

Now for each pair Equals was executed, and you've got your grouping.

可是……

什么是平等"?如果再添加一个点(2.1, 0.0),你希望在一组中加入哪些点?使用符号 表示模糊等式,我们有 -

What is "equal"? If you add another point (2.1, 0.0), which points do you want in one group? Using the symbol for the fuzzy equality, we have -

1.1 ≈ 1.2
1.2 ≈ 2.1

但是

1.1 !≈ 2.1

这意味着 1.12.1 永远不会在一个组中(他们的 Equals 永远不会通过)并且 这取决于点的顺序无论是1.1还是2.1都与1.2分组.

This means that 1.1 and 2.1 will never be in one group (their Equals never passes) and that it depends on the order of the points whether 1.1 or 2.1 are grouped with 1.2.

所以你在这里很滑.按邻近度聚类点绝非易事.您正在进入集群分析的领域.

So you're on a slippery slope here. Clustering points by proximity is far from trivial. You're entering the realm of cluster analysis.

这篇关于为 linq groupby 编写自定义比较器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆