带有 linq/lamba 表达式的空 while 循环
在我工作的地方,我们仍然使用 .Net 2.0,但我对 3.0/3.5 的东西有些熟悉。 我想在 C# 中使用 linq/lambda 表达式进行一些练习,因此我使用大量通用 List
在我的解算器中,我在解算器算法的顶部有这个:
private Puzzle RecursiveSolve(Puzzle p, int idx)
{
// start with simple filter to narrow down recursive paths
// puzzle is still solved without this line, but it takes at least 20x longer
while( p.board.Any(cell => cell.FilterPossibles()) ); // while(); <- empty while loop is intentional
正如您可能知道的那样,这是一个简单的递归算法,但我确实对其进行了一些优化,以将运行时间降低到合理的水平(最难的谜题为 3.6 秒)我可以在谷歌上找到)。
为了理解该代码段,Puzzle.board
是一个 List
,并且 Cell.FilterPossibles()
根据值检查每个单元格的可能值相同行、列和 3x3 框中的其他单元格,看看是否可以消除任何单元格。 如果只剩下一种可能,它就会设置单元格的值并返回 true。 否则返回 false。 因此,只要板上至少有一个单元格在上一次迭代中发生更改(已解决),那么 while 循环就会运行。
我担心的是 while 循环是空的。 它相当于一种代码味道,告诉我我可能遗漏了一些东西。 有没有办法可以将其写为语句而不是循环?
作为这个项目的结果,我实际上有一大堆问题:(我是否在不知不觉中实现了一些可以告诉编译器的接口?我的函数选择通常合适吗?我有很多布尔方法来简化表达式:如何那更好吗?我怎样才能更好地构建它以可能使用不可变的 Puzzle/Cell 对象?)但这是现在最困扰我的一个。
Where I work we're still on .Net 2.0, but I'm somewhat familiar with the 3.0/3.5 stuff. I wanted to get some practice using linq/lambda expression in C# so I wrote a simple Sudoku solver using a lot of generic List<T>
and lambda expressions with the aggregate methods it provides.
In my solver, I have this at the top of the solver algorithm:
private Puzzle RecursiveSolve(Puzzle p, int idx)
{
// start with simple filter to narrow down recursive paths
// puzzle is still solved without this line, but it takes at least 20x longer
while( p.board.Any(cell => cell.FilterPossibles()) ); // while(); <- empty while loop is intentional
As you can probably tell, it's a simple recursive algorithm, but I did optimize it a bit to get the run time down to something reasonable (3.6s for the toughest puzzle I could find on google).
To understand the snippet, Puzzle.board
is a List<Cell>
, and Cell.FilterPossibles()
checks each cell's possible values against the values of other Cells in the same row, column, and 3x3 box to see if it can eliminate any. If it gets down to one possible it sets the value for the cell and returns true. Otherwise it returns false. So that while loop will run as long as at least one cell on the board changed (was solved) with the previous iteration.
What concerns me is that the while loop is empty. It amounts to a code smell and tells me I'm probably missing something. Is there a way I could write this as a statement, rather than a loop?
I actually have a whole bunch of questions as the result of this project: (Have I unknowingly implemented some interfaces I could tell the compiler about? Are my function choices generally appropriate? I have a lot of boolean methods to simplify the expressions: how could that be better? How could I better architect this to possible use immutable Puzzle/Cell objects?) But this is the one that's nagging at me the most right now.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
空的 while 循环表明存在错误或副作用 - 在这种情况下,听起来像是副作用,改变了板子。
“类似 LINQ”(但不一定高效 - 这实际上取决于)是让谜题产生板的迭代器 - 执行另一次迭代将返回一个新板(无副作用) )。
我对你正在做的事情不太“满意”,所以它可能不合适——但这将是我的第一个问题。
除此之外,我可能会考虑以某种方式重写它,以便更明显地表明您确实有副作用 - 但同样,在不知道您的代码的情况下,很难确切地说出它是如何工作的。
An empty while loop suggests either a bug or side-effects - in this case it sounds like it's side-effects, changing the board.
What would be "LINQ-like" (but not necessarily efficient - it really depends) is to make the puzzle yield an iterator of boards - performing another iteration would return a new board (side-effect-free).
I'm not really "up" with exactly what you're doing, so it may not be appropriate - but that would be my first line of enquiry.
Other than that, I'd probably think about rewriting it somehow to make it more obvious that you do have side-effects - but again, without knowing your code it's hard to say exactly how that would work.
我并不是真的反对空循环,我在 C++ 中相对经常使用它们——尽管将主体限定为
{ }
,恕我直言,这更清晰。但是,我没有得到你的代码,这似乎表明确实存在代码味道,或者至少可以以更清晰的方式制定它。
我特别不明白的是
Any
返回单个布尔值,once。 这如何与循环结合在一起? 这里的副作用确实不明显(你修改了p.board
的内容?)……不知何故,似乎太聪明了。I'm not really opposed to empty loops, I use them relatively often in C++ – albeit qualifying the body as
{ }
which is much clearer IMHO.However, I don't get your code which seems to indicate that there's indeed code smell or at least that it could be formulated in a clearer way.
What I don't get in particular is the fact that
Any
returns a single boolean value, once. How does this tie together with a loop? The side-effect here is really non-obvious (you modify the contents ofp.board
?) … seems too clever, somehow.我认为具有适当名称的提取方法在这里可能会有用。 它不会消除气味,但您可以将其从 RecursiveSolver 中的第一行移开。 (我可以想象读完它后会立即碰壁)。
拥有像这样简洁的一个衬里是可以的,但是使用一个命名良好的方法看起来会更好...
重命名 FilterPossibles() 可以创造奇迹...该方法的作用超出了它的说法...
I think an Extract Method with an appropriate name might be useful here. It won't eliminate the smell but you can move it away from the very first line in your RecursiveSolver. (I could imagine reading it and hitting a brick wall right away).
It is OK to have terse one liners like this but it would look better with a well named method...
Renaming FilterPossibles() could do wonders...That method is doing more than its saying...