использование goto против оценки кода во время выполнения - PullRequest
4 голосов
/ 11 сентября 2009

Недавно для класса программирования нам было дано задание написать программу на любом языке, который при n произведет все возможные отклонения для массива p размера. n такое, что p [i]! = I для всех i: 0 <= i <n. Нам пришлось использовать итераторы, например, <code>yield.

Пример: n = 3, [0, 1, 2] не является нарушением, но [2, 0, 1] также как и [1, 2, 0].

Я предложил решение с псевдокодом, которое бы работало, но проблема заключалась в том, что для этого требовались петли питания (т. Е. n вложенные петли, в которых n известен только во время выполнения) , Для этого я сгенерировал n вложенных циклов в коде Ruby в строке, а затем eval -ed строку. Мое решение работало, однако мой профессор подумал, что использование нескольких goto с было бы лучшим решением (по крайней мере, легче читаемым), чем динамическая генерация кода.

У меня сложилось впечатление, что goto всегда был плохим выбором. Почему оценка динамически сгенерированного кода во время выполнения может быть хуже, чем goto? Сгенерированный код является чистым и простым, и кажется довольно эффективным для данной проблемы. Единственный пользовательский ввод, от которого зависит генерация кода, это n , который проверяется, чтобы заранее убедиться, что это целочисленное значение. Это yield только уникальные расстройства, как и должно быть.

Я не прошу решения моего программного задания, я просто хочу знать причины использования goto для динамической оценки кода или наоборот.

Редактировать: , чтобы уточнить, назначение включало в себя написание программы с использованием итераторов, а другой - с использованием рекурсии, поэтому итеративная версия не обязательно должна быть эффективной.

Ответы [ 11 ]

6 голосов
/ 11 сентября 2009

И GOTO, и генерация кода являются нелегкими решениями этой проблемы IMO. Существует рекурсивный алгоритм, который, вероятно, является правильным ответом здесь.

2 голосов
/ 11 сентября 2009

Это действительно интересный вопрос - я не уверен, что есть однозначный ответ.

Проблема с goto заключается в его неструктурированном использовании: goto - это «массивный случайный скачок», поэтому в общем случае после прыжка вы не знаете, откуда вы пришли, что вызывает все виды проблем. с точки зрения отладки и обслуживания и - в более формальном смысле с доказательством «правильности» кода. Конечно, есть языки (я уже давно), где у вас нет возможности, и в этот момент вы навязываете структуру кода. Суть в том, что GOTO не так плох, как способ использования (и злоупотребления) goto, который плох и делает его опасной конструкцией доступной.

Использование генерации кода и последующая оценка результата - это умно :) Однако «умно» не всегда хорошо, и я подозреваю, что отчасти проблема с использованием этого в качестве решения заключается в том, что он фактически не решает проблему, как предполагалось. Это может быть «обманом» в некотором смысле - по крайней мере, в том, что касается вашего профессора - не лишает законной силы ваше решение, но может сделать его «не легким». Проблемы с отладкой и сопровождением также возникают в отношении кода.

Рекурсивное решение - тем более, что я смутно помню, как меня учили (около 25 лет назад), что обычно можно развернуть рекурсию в циклы - возможно, было бы самым элегантным.

Определенно интересный вопрос!

2 голосов
/ 11 сентября 2009

Динамический код не проверяется во время компиляции, что означает, что любые ошибки останутся незамеченными до времени выполнения. Потенциально затрудняет их поиск. Для ruby ​​это будет означать, что синтаксические ошибки не будут обнаружены IDE или редактором, в зависимости от того, что вы используете. Это плюс для выбора goto.

Я думаю, мне нужно было бы увидеть и то и другое, чтобы принять решение в этом случае. Я не видел код, но готов поспорить, что есть хорошее решение, которое не использует динамический код или метод goto. goto - это не всегда плохо, но если вы думаете об его использовании, вы, вероятно, не приняли лучших дизайнерских решений до этого момента и, вероятно, хотите пересмотреть свое решение.

2 голосов
/ 11 сентября 2009

Вы можете решить практически все проблемы без использования GoTos. В частности, с циклами вы можете использовать операторы break и continue для косвенного использования gotos, пока стандарт кода все еще поддерживается.

n вложенные циклы звучат как плохой план, и я предлагаю вместо этого взглянуть на рекурсивные функции. Каждый раз, когда вам нужно сделать n-циклы, вы всегда должны думать о рекурсии.

2 голосов
/ 11 сентября 2009

Не видя ваш код, я бы склонялся на сторону проф. Если бы это был выбор между GoTo и динамическим кодом, я бы склонялся к первому. GoTo не всегда плохой выбор.

1 голос
/ 12 сентября 2009

GOTO Solution - функция goto удобна при моделировании вызова функции. Вот нерекурсивное решение, которое просто имитирует рекурсивное решение, используя стек и метку перехода, чтобы вернуться к точке, где произошел бы вызов функции.

См. Рекурсивную процедуру (я разместил это как отдельный ответ) для сравнения.

Опция Строгое Включено Опция Явная Вкл.

Модуль Модуль 1 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

Конечный модуль

1 голос
/ 11 сентября 2009

Основная причина, по которой люди избегают утверждений goto, заключается в том, что они могут сделать вашу программу более трудной для понимания.

Не увидев ваш код, я думаю, это сложнее понять, чем эквивалентная программа, использующая goto ...

1 голос
/ 11 сентября 2009

В одном из моих заданий в колледже мне когда-то приходилось делать что-то относительно похожее Мое решение состояло в том, чтобы использовать рекурсивную функцию, передавая массив, размер массива и уровень вложенности в качестве аргумента. Затем функция вызывает себя с уровнем вложенности +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 в своей жизни, поэтому я уверен, что всегда есть способ избежать их

0 голосов
/ 12 сентября 2009

Рекурсивное решение - вот решение с ранней обрезкой. Мой единственный вопрос касается перечислений: он хотел, чтобы вы создали перечислитель, который при каждом успешном вызове генерировал бы следующий элемент в списке? Это, вероятно, было бы проще всего реализовать, создав лямбда-версию этой программы - я делал это в lisp все время при написании генераторов запросов, которые генерировали бы следующий ответ на вопрос при выполнении запросов в стиле пролог-интерпретатора.

Опция Строгое Включено Опция Явная Вкл.

Модуль Модуль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

Конечный модуль

0 голосов
/ 11 сентября 2009

Как решения goto, так и идеи динамической генерации кода плохие. Это легко решается с помощью рекурсивного решения, как уже упоминалось другими. Вы просто рекурсивно генерируете все перестановки (стандартное рекурсивное решение), а когда генерация завершена (т.е. на листе рекурсии), просто пропускаете возвращающиеся перестановки, которые не являются отклонениями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...