Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为linq groupby编写自定义比较器_Linq_Iequalitycomparer - Fatal编程技术网

为linq groupby编写自定义比较器

为linq groupby编写自定义比较器,linq,iequalitycomparer,Linq,Iequalitycomparer,同样,这个示例是我实际问题的一个非常简化的版本,涉及用于linq分组的自定义比较器。我做错了什么 下面的代码生成下面的结果 (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) 类程序 { 静态void Main(字符串[]参数) { IEnumerable points=新列表{ 新点(1.1,0.0) ,新点(4.1,0.0) ,

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

下面的代码生成下面的结果 (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)

类程序
{
静态void Main(字符串[]参数)
{
IEnumerable points=新列表{
新点(1.1,0.0)
,新点(4.1,0.0)
,新点(1.2,0.0)
,新点(4.1,0.0)
};
foreach(points.GroupBy中的var组(p=>p,new PointComparer())
{
foreach(组中的var num)
Write(num.ToString()+“,”);
Console.WriteLine();
}
Console.ReadLine();
}
}
类指针比较器:IEqualityComparer
{
公共布尔等于(a点,b点)
{
返回Math.Abs(a.X-b.X)<1.0;
}
public int GetHashCode(Point-Point)
{
返回点.X.GetHashCode()
^point.Y.GetHashCode();
}
}
类点
{
公共双X;
公共双Y;
公共点(双p1,双p2)
{
X=p1;
Y=p2;
}
公共重写字符串ToString()
{
返回“(“+X+”、“+Y+”)”;
}
}

不要忘记
GetHashCode
的效果。对于任何两个对象,
GetHashCode
总是会返回相同的值,因为每个
Equals
都会返回true。如果你没有达到预期,你会得到意想不到的结果

具体地说,
GroupBy
可能使用类似哈希表的东西将项目分组在一起,而无需将每个项目与每个其他项目进行比较。如果
GetHashCode
返回一个值,但该值不会最终将两个对象放入哈希表的同一个存储桶中,它将假定它们不相等,并且永远不会尝试对它们调用
Equals

当您试图找出
GetHashCode
的正确实现时,您会发现如何对对象进行分组存在一个基本问题。如果点的x值分别为
1.0
1.6
2.2
,您会期望什么
1.0和
2.2
距离太远,无法归入同一组,但
1.6
距离其他两个点足够近,因此应与它们位于同一组。因此,您的
Equals
方法打破了相等的属性:

只要A=B和B=C,那么A=C

如果您试图进行集群分组,则需要使用更不同的数据结构和算法。如果您只是尝试对点的位置进行某种规格化,那么您可以只说
points.GroupBy(p=>(int)p.X)
,完全避免使用相等比较器。

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

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();
    }
}
仅对哈希代码相等的两点执行
Equals

现在,您可以通过始终将
0
作为哈希代码返回来欺骗比较。如果这样做,输出将是:

HashCode:(1.1,0)
哈希代码:(4.1,0)
等于:点(1.1,0)-点(4.1,0)
哈希代码:(1.2,0)
等于:点(4.1,0)-点(1.2,0)
等于:点(1.1,0)-点(1.2,0)
哈希代码:(4.1,0)
等于:点(4.1,0)-点(4.1,0)
(1.1, 0), (1.2, 0), 
(4.1, 0), (4.1, 0), 
现在,对每一对执行
Equals
,您就得到了分组

但是

什么是“平等”?如果添加另一个点
(2.1,0.0)
,您希望在一个组中添加哪些点?使用符号

1.1≈ 1.2
1.2≈ 2.1
但是

1.1!≈ 2.1
这意味着
1.1
2.1
永远不会在一组中(它们的
等于
永远不会通过),这取决于点的顺序
1.1
2.1
是否与
1.2
分组


所以你在这里走下坡路。通过邻近性对点进行聚类绝非易事。您正在进入。

我认为您无法使用group by作为群集点的解决方案。一个原因是GetHashcode必须为相等的项返回相同的哈希值。我知道这里没有这样做,但需要注意的是:不要使用
PointComparer.Default
而不是
new PointComparer()
——不管文档怎么说,它不会创建一个新的
指针比较器
,而是创建一个
ObjectEqualityComparer`1
。这正是我所看到的,我的GetHasCode对这个问题有重大影响。我对它所做的任何更改都会导致输出的更改。我的GetHashCode方法应该是什么样子?@DustyB:请看我更新的答案。你目前对两项“相等”的定义是站不住脚的。你必须想出一个更具体的想法来对项目进行分组,或者使用不同的数据结构和算法,基于聚类而不是相等。我的实际问题是使用3D点,我想对它们进行聚类,使10个单位内的点和同一平面上的点组合在一起。用自定义比较器做这件事可行吗?@DustyB:不可靠。如果A点和B点之间的距离在10个单位以内,B点和C点之间的距离在10个单位以内,但A点与C点之间的距离不在10个单位以内,那么它们是否都应该在一起?无论您如何回答这个问题,您选择的任何纯粹基于相等的分组算法都将无法在以下条件下生成您想要的输出
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();
    }
}