使用 goto 与运行时代码评估
题
最近,在编程课上,我们被分配用任何语言编写一个程序,给定 n, ,将为数组产生所有可能的混乱 p 尺寸的 n 这样 p[i] != i 对于所有 i:0 <= i < n。我们必须使用迭代器,例如 yield
.
例子:n=3, [0, 1, 2] 不是乱序,但 [2, 0, 1] 和 [1, 2, 0] 一样。
我想出了一个可行的伪代码解决方案,但问题是它需要电源循环(即, n 嵌套循环,其中 n 仅在运行时才知道)。为此,我生成了 n 在字符串中的 Ruby 代码中嵌套循环,然后 eval
-ed 字符串。我的解决方案有效,但是我的教授认为使用一些 goto
s 将是比动态代码生成更好的解决方案(至少更容易阅读)。
我的印象是 goto
总是一个糟糕的选择。为什么动态生成的代码的运行时评估可能是比 goto
?生成的代码干净简单,对于给定的问题似乎相当有效。代码生成所依赖的唯一用户输入是 n, ,预先检查以确保它是整数值。它 yield
这只是独特的混乱,正如它应该的那样。
我不是要求解决我的编程作业,我只是想知道使用背后的原因 goto
超过动态代码评估,反之亦然。
编辑: 需要澄清的是,作业包括使用迭代器编写一个程序和使用递归编写另一个程序,因此迭代版本不一定是高效的。
解决方案
这是一个非常有趣的问题 - 我不确定是否有明确的答案。
goto的问题在于它以非结构化的方式使用 - goto是<!>“;大量随机跳跃<!>”;所以在一般情况下,在跳转之后,你不知道你来自哪里导致调试和可维护性方面的各种问题,并且 - 在更正式的意义上证明<!>正确性<!> QUOT;的代码。当然有些语言(我已经有一段时间了)你没有选项,你可以在这个位置强加代码的结构。最重要的是,GOTO并不是很糟糕,因为goto的使用方式(并且被滥用)很糟糕,这使得它成为一个危险的构造。
使用代码生成然后评估结果很聪明:)但是<!>“聪明的<!>”;并不总是一件好事,我怀疑在某种程度上使用它作为解决方案的问题是它实际上没有按预期解决问题。这可能是<!>“作弊<!>”;从某种意义上说 - 至少就你的教授而言 - 不会使你的解决方案无效,但可能会使它成为<!>“不优雅的<!>”。调试和维护问题也出现在代码中。
递归解决方案 - 特别是当我依旧记得被教导(大约25年前),通常可以将递归放入循环中 - 可能是最优雅的。
绝对是一个有趣的问题!
其他提示
GOTO和代码生成都是IMO这个问题的优雅解决方案。这里有一个递归算法可能是正确的答案。
没有看到你的代码,我倾向于支持教授。如果它是GoTo和动态代码之间的选择,我会倾向于前者。 GoTo 总是是一个糟糕的选择。
您可以在不使用GoTos的情况下解决几乎所有问题。特别是使用循环,你可以使用break和continue语句来暗示使用gotos,同时仍然保持代码标准。
n 嵌套循环听起来像一个糟糕的计划,我建议你改为查看递归函数。每次你需要做一个n循环时,你应该总是考虑递归。
动态代码在编译时不可检查,这意味着在运行时之前不会检测到任何错误。可能会使它们更难找到。对于 ruby,这意味着 IDE 或编辑器不会发现语法错误,无论您使用的是哪一个。这是选择 goto 的一个优点。
我认为在这种情况下我必须同时考虑双方的意见才能做出决定。我还没有看到代码,但我愿意打赌有一个很好的解决方案,不使用动态代码或 goto。goto 并不总是不好,但如果您正在考虑使用它,那么到目前为止您可能还没有做出最佳的设计决策,并且可能想要重新审视您的解决方案。
在我上大学的一个学期,我曾经做过一些相对相似的事情 我的解决方案是使用递归函数,传递数组,数组的大小和嵌套级别作为参数。然后,该函数使用嵌套级别+1调用自身,直到嵌套级别等于数组的大小。没有Goto,没有代码评估,只有干净的代码!
示例
function computeDerangement(yourArray, loopLevel, arraySize)
{
//We check to see if the loop level is the same as the array size
//if true, then we have executed exactly n loop
if (loopLevel == arraySize) {
display(yourArray); //Display being some kind of function that show the array,
//you get the idea
} else {
while(something) {
//Here you put the logic that you execute at one level of the loop
//Then you call yourself with one more level of nesting
computeDerangement(yourArray, loopLevel + 1, arraySize);
}
}
}
希望有所帮助!
我从来没有在生活中使用过goto,所以我很确定总有一种方法可以避免它们
人们避免使用goto语句的主要原因是它们会使您的程序更难理解。
没有看过你的代码,我猜它比使用goto的等效程序更难理解......
GOTO解决方案 - 模拟函数调用时,goto很方便。这是一个非重新解决方案,它使用堆栈和goto标签简单地模拟递归解决方案,以返回到函数调用发生的位置。
请参阅递归过程(我已将此作为单独的答案发布)以进行比较。
选项严格打开 选项明确的
模块Module1 Dim x As Stack
Private Sub printGeneratedList(ByVal generatedList As List(Of Integer))
For Each el In generatedList
Console.Write(el & " ")
Next
Console.WriteLine()
End Sub
Private Sub generateAux(ByVal i As Integer, ByVal n As Integer, _
ByVal generatedList As List(Of Integer))
Dim stackI As Stack(Of Integer) = New Stack(Of Integer)
Dim stackJ As Stack(Of Integer) = New Stack(Of Integer)
Dim j As Integer
STARTLABEL:
j = 0
If i >= n Then
printGeneratedList(generatedList)
If stackI.Count = 0 Then
Return
Else
GoTo ReturnLabel
End If
End If
While j < n
If Not j = i Then
If Not generatedList.Contains(j) Then
generatedList.Add(j)
stackI.Push(i)
stackJ.Push(j)
i = i + 1
GoTo StartLabel
ReturnLabel:
i = stackI.Pop()
j = stackJ.Pop()
generatedList.Remove(j)
End If
End If
j = j + 1
End While
If stackI.Count = 0 Then
Return
Else
GoTo ReturnLabel
End If
End Sub
Private Sub generate(ByVal n As Integer)
Console.WriteLine("Generating for n = " & n.ToString())
Dim l As List(Of Integer) = New List(Of Integer)
If n < 0 Then
Throw New Exception("n must be >= 0")
End If
generateAux(0, n, l)
End Sub
Sub Main()
generate(0)
Console.ReadLine()
generate(1)
Console.ReadLine()
generate(2)
Console.ReadLine()
generate(3)
Console.ReadLine()
generate(4)
Console.ReadLine()
End Sub
结束模块
goto不干净。但如果需要高性能,有时需要打破一些编码规则......
如果速度确实是一个重要的问题,例如,如果你想要编写一个你有很大特权的库或代码,你可以考虑使用goto。当然,你必须注意一切顺利。
注释:最后,执行的CPU只执行简单的跳转。只是编程语言/编译器创建它们。谨慎使用,不要惹它。
goto解决方案和动态代码生成方法都很糟糕。这可以通过其他人提到的递归解决方案轻松解决。您只需递归生成所有排列(标准递归解决方案),并在生成完成时(即在递归的叶子处)简单地跳过返回非紊乱的排列。
递归解决方案 - 这是一个早期修剪的解决方案。我唯一的问题是关于枚举:他是否希望你创建一个枚举器,在每次成功调用时都会生成列表中的下一个项目?这可能是通过创建此程序的lambda版本最容易实现的 - 我在编写查询生成器时始终在lisp中执行此操作,这些查询生成器在执行prolog-interpreter样式查询时将生成问题的下一个答案。
选项严格打开 选项明确的
模块模块1
Private Sub printGeneratedList(ByVal generatedList As List(Of Integer))
For Each el In generatedList
Console.Write(el & " ")
Next
Console.WriteLine()
End Sub
Private Sub generateAux(ByVal i As Integer, ByVal n As Integer, _
ByVal generatedList As List(Of Integer))
If i >= n Then
printGeneratedList(generatedList)
Return
End If
For j As Integer = 0 To n - 1
If Not j = i Then
If Not generatedList.Contains(j) Then
generatedList.Add(j)
generateAux(i + 1, n, generatedList)
generatedList.Remove(j)
End If
End If
Next
End Sub
Private Sub generate(ByVal n As Integer)
Console.WriteLine("Generating for n = " & n.ToString())
Dim l As List(Of Integer) = New List(Of Integer)
If n < 0 Then
Throw New Exception("n must be >= 0")
End If
generateAux(0, n, l)
End Sub
Sub Main()
generate(0)
Console.ReadLine()
generate(1)
Console.ReadLine()
generate(2)
Console.ReadLine()
generate(3)
Console.ReadLine()
generate(4)
Console.ReadLine()
End Sub
结束模块