C# IEnumerable、Where和Object.ReferenceEquals存在问题
试图解决一个问题(多米诺骨牌)。在这个问题中,我试图创建一个有效的多米诺骨牌链,使数字相互接触,并且第一个和最后一个数字相同 示例:C# IEnumerable、Where和Object.ReferenceEquals存在问题,c#,.net-core,ienumerable,.net-core-3.0,C#,.net Core,Ienumerable,.net Core 3.0,试图解决一个问题(多米诺骨牌)。在这个问题中,我试图创建一个有效的多米诺骨牌链,使数字相互接触,并且第一个和最后一个数字相同 示例:[1 | 2]、[3,1]、[3,2]->[1 | 2][2 | 3][3 | 1] 我使用蛮力来尝试所有的组合。为此,我使用以下代码启动一个包含所有可能组合的链: using System; using System.Collections.Generic; using System.Linq; public static class Dominoes {
[1 | 2]、[3,1]、[3,2]->[1 | 2][2 | 3][3 | 1]
我使用蛮力来尝试所有的组合。为此,我使用以下代码启动一个包含所有可能组合的链:
using System;
using System.Collections.Generic;
using System.Linq;
public static class Dominoes
{
private class Domino
{
public readonly int First;
public readonly int Last;
public Domino(int first, int last)
{
First = first;
Last = last;
}
public Domino Flip() => new Domino(Last, First);
}
private class Chain
{
private readonly List<Domino> chained = new List<Domino>();
private readonly List<Domino> remaining = new List<Domino>();
private int First => chained[0].First;
private int Last => chained[chained.Count - 1].Last;
public bool Complete => remaining.Count == 0 && First == Last;
private Chain(Domino domino, IEnumerable<Domino> remaining, IEnumerable<Domino> chained = null)
{
if (chained != null)
{
this.chained.AddRange(chained);
}
this.chained.Add(domino);
this.remaining.AddRange(remaining);
}
public static IEnumerable<Chain> Start(IEnumerable<Domino> dominoes)
{
var chains = new List<Chain>();
foreach (var domino in dominoes)
{
var remaining = dominoes.Where(d => d != domino);
chains.Add(new Chain(domino, remaining));
chains.Add(new Chain(domino.Flip(), remaining));
}
return chains;
}
public IEnumerable<Chain> Extend()
{
var chains = new List<Chain>();
foreach (var domino in this.remaining)
{
var remaining = this.remaining.Where(d => d != domino);
if (domino.First == Last)
{
chains.Add(new Chain(domino, remaining, chained));
}
if (domino.Last == Last)
{
chains.Add(new Chain(domino.Flip(), remaining, chained));
}
}
return chains;
}
}
public static bool CanChain(IEnumerable<(int, int)> dominoes)
{
var chains = Chain.Start(dominoes.Select(d => new Domino(d.Item1, d.Item2)));
return chains.Any() ? Iterate(chains) : true;
}
private static bool Iterate(IEnumerable<Chain> chains)
{
var newChains = new List<Chain>();
foreach (var chain in chains)
{
if (chain.Complete) return true;
newChains.AddRange(chain.Extend());
}
if (newChains.Count == 0) return false;
return Iterate(newChains);
}
}
private class Foobar {
private readonly int value;
public Foobar(int v) {
Console.WriteLine("## CONSTRUCTOR ## Foobar object created with value: "+v);
this.value = v;
}
public override string ToString() {
return "Foobar("+this.value+")";
}
}
public static void Main(string[] args) {
int[] numbers = new [] { 1, 5};
IEnumerable<Foobar> range = numbers.Select(i => new Foobar(i));
Console.WriteLine(range.Count());
foreach (Foobar entry in range) {
Console.WriteLine("Build an enumerable without "+entry);
IEnumerable<Foobar> remaining = range.Where(it => it != entry);
Console.WriteLine("The length of the remaining: "+remaining.Count());
foreach (Foobar remainingEntry in remaining) {
Console.WriteLine("Entry of remaining: "+remainingEntry);
}
}
}
使用dotnet测试运行
对链中剩余的筛选。开始似乎不起作用
示例(使用new[]{(1,2)}
作为CanChain
的输入)
如果我在多米诺骨牌上调用.ToList()
。在CanChain
中选择(d=>newdomino(d))
,它将开始工作
编辑:添加了更多信息问题在于,Select
的枚举没有立即执行,而是延迟执行。这意味着每次访问/使用Select()
方法返回的IEnumerable
时,您都会得到一个新的列表。但这也意味着您一直在创建新的Domino
对象。由于您没有覆盖Equals()
方法,因此这些Domino
对象中的每一个都将不同,即使它们具有相同的值
要显示问题,请检查以下代码:
using System;
using System.Collections.Generic;
using System.Linq;
public static class Dominoes
{
private class Domino
{
public readonly int First;
public readonly int Last;
public Domino(int first, int last)
{
First = first;
Last = last;
}
public Domino Flip() => new Domino(Last, First);
}
private class Chain
{
private readonly List<Domino> chained = new List<Domino>();
private readonly List<Domino> remaining = new List<Domino>();
private int First => chained[0].First;
private int Last => chained[chained.Count - 1].Last;
public bool Complete => remaining.Count == 0 && First == Last;
private Chain(Domino domino, IEnumerable<Domino> remaining, IEnumerable<Domino> chained = null)
{
if (chained != null)
{
this.chained.AddRange(chained);
}
this.chained.Add(domino);
this.remaining.AddRange(remaining);
}
public static IEnumerable<Chain> Start(IEnumerable<Domino> dominoes)
{
var chains = new List<Chain>();
foreach (var domino in dominoes)
{
var remaining = dominoes.Where(d => d != domino);
chains.Add(new Chain(domino, remaining));
chains.Add(new Chain(domino.Flip(), remaining));
}
return chains;
}
public IEnumerable<Chain> Extend()
{
var chains = new List<Chain>();
foreach (var domino in this.remaining)
{
var remaining = this.remaining.Where(d => d != domino);
if (domino.First == Last)
{
chains.Add(new Chain(domino, remaining, chained));
}
if (domino.Last == Last)
{
chains.Add(new Chain(domino.Flip(), remaining, chained));
}
}
return chains;
}
}
public static bool CanChain(IEnumerable<(int, int)> dominoes)
{
var chains = Chain.Start(dominoes.Select(d => new Domino(d.Item1, d.Item2)));
return chains.Any() ? Iterate(chains) : true;
}
private static bool Iterate(IEnumerable<Chain> chains)
{
var newChains = new List<Chain>();
foreach (var chain in chains)
{
if (chain.Complete) return true;
newChains.AddRange(chain.Extend());
}
if (newChains.Count == 0) return false;
return Iterate(newChains);
}
}
private class Foobar {
private readonly int value;
public Foobar(int v) {
Console.WriteLine("## CONSTRUCTOR ## Foobar object created with value: "+v);
this.value = v;
}
public override string ToString() {
return "Foobar("+this.value+")";
}
}
public static void Main(string[] args) {
int[] numbers = new [] { 1, 5};
IEnumerable<Foobar> range = numbers.Select(i => new Foobar(i));
Console.WriteLine(range.Count());
foreach (Foobar entry in range) {
Console.WriteLine("Build an enumerable without "+entry);
IEnumerable<Foobar> remaining = range.Where(it => it != entry);
Console.WriteLine("The length of the remaining: "+remaining.Count());
foreach (Foobar remainingEntry in remaining) {
Console.WriteLine("Entry of remaining: "+remainingEntry);
}
}
}
正如您所见,构造函数被调用的次数太多了。每次有人使用Select()
调用中的IEnumerable
时,它将再次生成可枚举列表,这样就不会干扰相同数据上的其他并行运行迭代。但是,由于在Select()
语句中创建了新对象,因此每次创建Select()
的IEnumerable
,都会得到新对象
正如您已经注意到的,要解决这个问题,您需要使用类似于ToList()
的方法,并一次性评估/创建Domino
对象。使用一个ToList()
调用输出更改,如下所示:
###构造函数##Foobar对象创建值:1
##构造函数##Foobar对象创建值:5
2.
构建不带Foobar的可枚举文件(1)
剩余的长度:1
输入剩余:Foobar(5)
构建不带Foobar的可枚举文件(5)
剩余的长度:1
输入剩余:Foobar(1)
你看,只创建了两个对象,并且=代码>将按预期工作,因为只有这两个对象。问题是,Select
的枚举没有立即执行,而是被延迟。这意味着每次访问/使用Select()
方法返回的IEnumerable
时,您都会得到一个新的列表。但这也意味着您一直在创建新的Domino
对象。由于您没有覆盖Equals()
方法,因此这些Domino
对象中的每一个都将不同,即使它们具有相同的值
要显示问题,请检查以下代码:
using System;
using System.Collections.Generic;
using System.Linq;
public static class Dominoes
{
private class Domino
{
public readonly int First;
public readonly int Last;
public Domino(int first, int last)
{
First = first;
Last = last;
}
public Domino Flip() => new Domino(Last, First);
}
private class Chain
{
private readonly List<Domino> chained = new List<Domino>();
private readonly List<Domino> remaining = new List<Domino>();
private int First => chained[0].First;
private int Last => chained[chained.Count - 1].Last;
public bool Complete => remaining.Count == 0 && First == Last;
private Chain(Domino domino, IEnumerable<Domino> remaining, IEnumerable<Domino> chained = null)
{
if (chained != null)
{
this.chained.AddRange(chained);
}
this.chained.Add(domino);
this.remaining.AddRange(remaining);
}
public static IEnumerable<Chain> Start(IEnumerable<Domino> dominoes)
{
var chains = new List<Chain>();
foreach (var domino in dominoes)
{
var remaining = dominoes.Where(d => d != domino);
chains.Add(new Chain(domino, remaining));
chains.Add(new Chain(domino.Flip(), remaining));
}
return chains;
}
public IEnumerable<Chain> Extend()
{
var chains = new List<Chain>();
foreach (var domino in this.remaining)
{
var remaining = this.remaining.Where(d => d != domino);
if (domino.First == Last)
{
chains.Add(new Chain(domino, remaining, chained));
}
if (domino.Last == Last)
{
chains.Add(new Chain(domino.Flip(), remaining, chained));
}
}
return chains;
}
}
public static bool CanChain(IEnumerable<(int, int)> dominoes)
{
var chains = Chain.Start(dominoes.Select(d => new Domino(d.Item1, d.Item2)));
return chains.Any() ? Iterate(chains) : true;
}
private static bool Iterate(IEnumerable<Chain> chains)
{
var newChains = new List<Chain>();
foreach (var chain in chains)
{
if (chain.Complete) return true;
newChains.AddRange(chain.Extend());
}
if (newChains.Count == 0) return false;
return Iterate(newChains);
}
}
private class Foobar {
private readonly int value;
public Foobar(int v) {
Console.WriteLine("## CONSTRUCTOR ## Foobar object created with value: "+v);
this.value = v;
}
public override string ToString() {
return "Foobar("+this.value+")";
}
}
public static void Main(string[] args) {
int[] numbers = new [] { 1, 5};
IEnumerable<Foobar> range = numbers.Select(i => new Foobar(i));
Console.WriteLine(range.Count());
foreach (Foobar entry in range) {
Console.WriteLine("Build an enumerable without "+entry);
IEnumerable<Foobar> remaining = range.Where(it => it != entry);
Console.WriteLine("The length of the remaining: "+remaining.Count());
foreach (Foobar remainingEntry in remaining) {
Console.WriteLine("Entry of remaining: "+remainingEntry);
}
}
}
正如您所见,构造函数被调用的次数太多了。每次有人使用Select()
调用中的IEnumerable
时,它将再次生成可枚举列表,这样就不会干扰相同数据上的其他并行运行迭代。但是,由于在Select()
语句中创建了新对象,因此每次创建Select()
的IEnumerable
,都会得到新对象
正如您已经注意到的,要解决这个问题,您需要使用类似于ToList()
的方法,并一次性评估/创建Domino
对象。使用一个ToList()
调用输出更改,如下所示:
###构造函数##Foobar对象创建值:1
##构造函数##Foobar对象创建值:5
2.
构建不带Foobar的可枚举文件(1)
剩余的长度:1
输入剩余:Foobar(5)
构建不带Foobar的可枚举文件(5)
剩余的长度:1
输入剩余:Foobar(1)
你看,只创建了两个对象,并且=代码>将按预期工作,因为只有这两个对象。Domino是定义为类还是结构?对象。只有当类(在本例中是Domino)重载了它的=
时,才需要ReferenceEquals
,这似乎不太可能。如果您能够显示Domino
代码,这将非常有帮助。同意不需要Object.Reference
。添加了Domino
code你能再发布一点代码吗?我们可以复制并运行它来产生意想不到的结果。调试会更容易。当我运行它并传入一个domino时,剩余的会像预期的那样变为空。domino是定义为类还是结构?对象。只有当类(本例中的domino)重载了它的=
时,才需要ReferenceEquals,这似乎不太可能。如果您能够显示Domino
代码,这将非常有帮助。同意不需要Object.Reference
。添加了Domino
code你能再发布一点代码吗?我们可以复制并运行它来产生意想不到的结果。调试这个会更容易。当我运行它并传入一个domino时,剩余的
会像预期的那样变成空的。我想它应该是沿着这些线的东西。我想它应该是沿着这些线的东西。