(现在我主要使用C#。欢迎使用其他语言的想法,但如果可以的话,请将它们翻译成C#,并且要明确。)
我一次又一次遇到的是嵌套循环,搜索一些2D数组以找到一个元素(通常是某个对象),然后必须对其进行操作。所以当然,一旦你找到那个对象,就应该打破这两个循环,这样你就不必不必要地继续搜索你已经找到的东西(特别是在可以遍历指数巨大数组的嵌套循环中)。
以下代码目前是我首选的方法:
Obj O = null;
bool KeepLooping = true;
for (int j = 0; j < height && KeepLooping; j++)
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
{
O = ObjArray[i, j]; // you found it, now remember it
KeepLooping = false; // clear the flag so the outer loop will break too
break;
}
}
}
感谢Erik Funkenbusch,如果我们这样做会变得更加优雅:
Obj O = null;
for (int j = 0; j < height && O == null; j++) // much, much better idea to check O for null in the outer loop
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
{
O = ObjArray[i, j]; // you found it, now remember it
break;
}
}
}
不再需要那个讨厌的额外布尔值!
然而,寻求替代或更好的解决方案仍在继续。多年来,我尝试了许多其他方法,但发现它们并不是因为某种原因而出现的那样:
j
(外循环迭代器)设置为高于height
的值,这将触发它自动中断。不理想,因为有时您想要记住您找到的i
和j
值。foreach
。不理想,因为foreach
不允许您操作集合(删除或添加它,这通常是我搜索对象的原因)。O
之外什么也不做的函数中。 return
有效地打破了两个循环。很多时候这没关系,但并非总是如此。我这样做是为了进行非常通用的搜索,但是还有很多&#34;组搜索&#34; 我希望集中搜索遍历。在这些情况下,我会找到2个或更多个对象(有时在同一个2D数组中),记住它们,然后才会突破这两个循环。goto
? (哇,这可能是goto的唯一合法使用吗?它比KeepLooping
标志更具可读性,特别是如果我们有3个或更多循环。)不理想,因为同事会尖叫血腥的谋杀。在C#中,goto
? return;
这可能会变得混乱。有时,对象操作涉及自己的循环。还有一个非常聪明的第7种方式,感谢User_PWY:
int size = width*height; // save this so you dont have to keep remultiplying it every iteration
for (int i = 0; i < size; i++)
{
int x = i % width; // ingenious method here
int y = i / width; // ingenious method here
O = ObjArray[x, y];
if (O != null)
break; // woohoo!
}
这有效地将2D数组压缩为一个for
循环以进行迭代。然而,一些批评指出,与仅使用i ++或j ++相比,mod和除法运算符相当慢,因此它可能会更慢(请记住我们正在处理谁知道什么大小的2D数组)。就像我评论的那样,应该有一种方法可以在一次操作中得到除法和余数,因为我非常确定x86汇编代码具有DIV操作码,它将商和余数存储在单独的寄存器中,所有这些都在一个DIV指令中。但是如何在C#中使用它,idk。
如果C#允许您命名循环(例如L1
和L2
),然后执行L1.break()
之类的操作,那将是很好的。无论你在哪个循环里面。唉......用这种语言做不到。 (使用宏可能会有一些秘密的方法吗?)是否会有一个C#6.0实现此功能?
编辑:在我看来,我判断他们的优雅和速度的解决方案。请记住,我们正在处理嵌套循环,这可能会成倍增长。额外的操作或比较可能会产生影响。
好吧,好吧,告诉我你喜欢的方式,特别是如果它没有列在这里。
答案 0 :(得分:2)
for (int i = 0; i < width*height; i++)
{
int x=i%width
,y=i/width;
//dostuff
}
我喜欢这种访问二维数组的方式。
评论1)
有许多评论担心mod(%)运算符可能代价高昂,但这是我们正在讨论的整数运算,我认为它应该与其他解决方案没有区别。
评论2)
关于宏。我找不到代码但设法简单地生成了一个代码。
#define FOR2DARRAY(WIDTH,HEIGHT)
\for (int i = 0, x = 0,y = 0; i < (WIDTH)*(HEIGHT); i++, x=i%(WIDTH),y=i/(HEIGHT))
答案 1 :(得分:1)
重构以避免深度嵌套,可能使用LINQ的切割器可能是更好的解决方案。
对于这种情况, Goto
是可能的(如果你不能提出更好的方法,这基本上只能使用它)。设置带有可读名称的标志可能会减少问题/尖叫。
不要
goto
更好的方法。 答案 2 :(得分:1)
goto
是一个非常好的解决方案,微软甚至recommends it:
goto语句将程序控件直接传送到带标签的 言。
goto的一个常见用途是将控制转移到特定的开关盒 标签或switch语句中的默认标签。
goto语句对于摆脱深层嵌套循环也很有用。
至于你关于对象破坏的问题,一旦你超出范围对象的范围,垃圾收集器应该将其标记为销毁,但是如果你使用的话,我无法确定行为是否相同,例如,using
循环for
指令和goto
,而不是正常退出范围。
答案 3 :(得分:1)
试试这个:
Obj O = ObjArray.Cast<Obj>().FirstOrDefault(obj => obj != null && obj.property == search_value);
这会将Obj
的2D数组转换为IEnumerable<Obj>
,并为您提供符合条件的第一个数组。如果它们都不匹配,则Obj O
设置为null
。
答案 4 :(得分:1)
也许我在你的选项列表中错过了它,但是为什么你不能将搜索重构为一个返回你的对象的方法呢?
private object FindObject(....)
{
for (int j = 0; j < height && KeepLooping; j++)
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property = search_value)
{
return ObjArray[i, j]; // you found it, now remember it
}
}
}
return null;
}
答案 5 :(得分:1)
有很多方法可以做到这一点。在你的例子中,我会这样做:
Obj O = null;
for (int j = 0; j < height && O == null; j++)
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
{
O = ObjArray[i, j]; // you found it, now remember it
break;
}
}
}
在这种情况下,KeepLooping变量是不必要的,因此简单的break语句非常优雅。
你甚至可以更简化这一点:
Obj O = null;
for (int j = 0; j < height && O == null; j++)
{
for (int i = 0; i < width && O == null; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property == search_value)
O = ObjArray[i, j]; // you found it, now remember it
}
}
现在,甚至不需要休息。
当然,这可能不适用于所有情况,但通常可以将结果类型用作状态值。
仅供参考,如果您正确地执行此操作,在foreach中修改集合的问题并不是真正的问题。例如:
// Assumes ObjArray is [,] and not [][]
foreach (int item in ObjArray.ToList()) // makes a copy to iterate over
{
if (item != null && item.property == search_value) {
ObjArray[0,0] = item; // I can now modify this for no reason
break;
}
}
答案 6 :(得分:0)
没有太大区别,但我更喜欢以这种方式使用布尔标志:
Obj O = null;
bool found = false;
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
if (ObjArray[i, j] != null && ObjArray[i, j].property = search_value)
{
O = ObjArray[i, j]; // you found it, now remember it
found = true; // clear the flag so the outer loop will break too
break;
}
}
if (found) break;
}
if (!found)
{
// The loop finished with no values found
}
else
{
// Do stuff here with the value found
}