使用goto与运行时代码评估

时间:2009-09-11 18:40:30

标签: algorithm loops iterator goto

最近对于一个编程类,我们被赋予编写任何语言的程序的任务,给定 n ,将产生数组 p 的所有可能的紊乱大小 n 使得 p [i]!= i对于所有i:0< = i< ñ。我们必须使用迭代器,例如yield

示例:n = 3,[0,1,2]不是紊乱,但[2,0,1]和[1,2,0]一样。

我提出了一个可行的伪代码解决方案,但问题是它需要电源循环(即 n 嵌套循环,其中 n 仅在运行)。为此,我在一个字符串中的Ruby代码中生成了 n 嵌套循环,然后eval - 编辑了字符串。我的解决方案有效,但是我的教授认为使用一些goto s会比动态代码生成更好的解决方案(至少更容易阅读)。

我的印象是goto总是一个糟糕的选择。为什么动态生成的代码的运行时评估比goto更糟糕呢?生成的代码简洁明了,对于给定的问题似乎相当有效。代码生成所依赖的唯一用户输入是 n ,检查它以确保它是预先的整数值。 yield只有它应该是唯一的紊乱。

我不是要求我的编程任务的解决方案,我只是想知道使用goto而不是动态代码评估的原因,反之亦然。

编辑:澄清一下,分配包括使用迭代器编写程序而另一个使用递归,因此迭代版本不一定非常有效。

11 个答案:

答案 0 :(得分:6)

GOTO和代码生成都是IMO这个问题的优雅解决方案。这里有一个递归算法可能是正确的答案。

答案 1 :(得分:2)

没有看到你的代码,我倾向于支持教授。如果它是GoTo和动态代码之间的选择,我会倾向于前者。 GoTo 总是是一个糟糕的选择。

答案 2 :(得分:2)

您可以在不使用GoTos的情况下解决几乎所有问题。特别是使用循环,你可以使用break和continue语句来暗示使用gotos,同时仍然保持代码标准。

n 嵌套循环听起来像一个糟糕的计划,我建议你改为查看递归函数。每次你需要做一个n循环时,你应该总是考虑递归。

答案 3 :(得分:2)

动态代码不是可编译的编译时间,这意味着在运行时之前不会检测到任何错误。可能使他们更难找到。对于ruby,这意味着IDE或编辑器无法找到语法错​​误,无论您使用哪个。这是选择goto的好处。

我认为在这种情况下,我必须看到两者都做出决定。我没有看到代码,但我愿意打赌有一个不使用动态代码或goto的好解决方案。 goto并不总是坏事,但是如果你正在考虑使用它,你可能还没有做出最好的设计决定,并且可能想要重温你的解决方案。

答案 4 :(得分:2)

这是一个非常有趣的问题 - 我不确定是否有明确的答案。

goto的问题在于它以非结构化的方式使用 - goto是一个“大规模的随机跳跃”所以在一般情况下,在跳转之后,你不知道你来自哪里导致各种问题在调试和可维护性方面 - 以更正式的方式证明代码的“正确性”。当然有些语言(我已经有一段时间了)你没有选项,你可以在这个位置强加代码的结构。最重要的是,GOTO并不是很糟糕,因为goto的使用方式(并且被滥用)很糟糕,这使得它成为一个危险的构造。

使用代码生成然后评估结果是聪明的:)然而“聪明”并不总是一件好事我怀疑部分使用它作为解决方案的问题是它实际上没有按预期解决问题。从某种意义上说,这可能是“作弊” - 至少就你的教授而言 - 不会使你的解决方案无效,但可能会使它“不优雅”。调试和维护问题也出现在代码中。

递归解决方案 - 特别是当我依旧记得被教导(大约25年前),通常可以将递归放入循环中 - 可能是最优雅的。

绝对是一个有趣的问题!

答案 5 :(得分:1)

在我上大学的一个学期,我曾经做过一些相对相似的事情 我的解决方案是使用递归函数,传递数组,数组的大小和嵌套级别作为参数。然后,该函数使用嵌套级别+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,所以我很确定总有一种方法可以避免它们

答案 6 :(得分:1)

人们避免使用goto语句的主要原因是它们会使您的程序更难理解。

没有看过你的代码,我猜它比使用goto的等效程序更难理解......

答案 7 :(得分:1)

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

结束模块

答案 8 :(得分:0)

goto不干净。但如果需要高性能,有时需要打破一些编码规则......

如果速度确实是一个重要的问题,例如,如果你想要编写一个你有很大特权的库或代码,你可以考虑使用goto。当然,你必须注意一切顺利。

注释:最后,执行的CPU只执行简单的跳转。只是编程语言/编译器创建它们。谨慎使用,不要惹它。

答案 9 :(得分:0)

goto解决方案和动态代码生成方法都很糟糕。这可以通过其他人提到的递归解决方案轻松解决。您只需递归生成所有排列(标准递归解决方案),并在生成完成时(即在递归的叶子处)简单地跳过返回非紊乱的排列。

答案 10 :(得分:0)

递归解决方案 - 这是一个早期修剪的解决方案。我唯一的问题是关于枚举:他是否希望你创建一个枚举器,在每次成功调用时都会生成列表中的下一个项目?这可能是通过创建此程序的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

结束模块