2014-09-16 5 views
4

При создании everthing с goto's легко (о чем свидетельствует f.ex.Il), мне было интересно, возможно ли также устранить все операторы goto с выражениями более высокого уровня и утверждениями - скажем - используя все, что поддерживается в Java.Можно ли всегда исключать goto?

Или, если хотите: то, что я ищу, это «переписать правила», которые всегда будут работать независимо от способа создания goto.

В основном это теоретический вопрос, чисто как интерес; а не как хорошие/плохие практики.

Очевидное решение, что я думал о том, чтобы использовать что-то вроде этого:

while (true) 
{ 
    switch (state) { 
     case [label]: // here's where all your goto's will be 
      state = [label]; 
      continue; 
     default: 
      // here's the rest of the program. 
    } 
} 

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

Итак, есть ли лучшее решение?


Update 1

Так много людей, кажется, думают, что вопрос «слишком широк», я собираюсь разработать немного больше ... причину я уже упоминал Java является потому, что Java не имеет оператора 'goto'. Как один из моих проектов по хобби, я пытался преобразовать код C# в Java, что оказалось довольно сложным (отчасти из-за этого ограничения в Java).

Это заставило меня задуматься. Если у вас есть f.ex. реализация метода «remove» в Open address (см.: http://en.wikipedia.org/wiki/Open_addressing - примечание 1), довольно удобно иметь «goto» в исключительном случае, хотя в этом конкретном случае вы можете переписать его, введя переменную «state» , Обратите внимание, что это всего лишь один пример: я реализовал генераторы кода для продолжения, которые производят тонны и тонны goto, когда вы пытаетесь их декомпилировать.

Я также не уверен, что переписывание в этом вопросе всегда будет исключать утверждение 'goto' и если оно разрешено в каждом случае. Хотя я не ищу формального «доказательства», некоторые доказательства того, что устранение возможно в этом вопросе, были бы замечательными.

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

+5

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

+0

Знаете ли вы, что 'while',' for' и другие петлевые конструкции внутренне используют goto? Итак, вы хотите избежать использования циклов? Попытка избежать оператора switch не имеет смысла. Ну, вы можете реорганизовать коммутатор на использование полиморфизма, но он должен быть по крайней мере один. –

+1

@SriramSakthivel Многие языки реализуют эти конструкции с помощью операторов перехода, но это не концептуальное требование; существуют и другие способы реализации этих конструкций, они просто не так широко используются в основных языках программирования. – Servy

ответ

8

В этом документе за 1994 год: Taming Control Flow: A Structured Approach to Eliminating Goto Statements предлагается алгоритм для устранения всех операторов goto в программе на C. Этот метод применим к любой программе, написанной на C# или любом языке, который использует общие конструкции, такие как if/switch/loop/break/continue (AFAIK, но я не понимаю, почему это не так).

Она начинается с двумя простейшими преобразованиями:

// CASE 1 

Stuff1(); 
if (cond) goto Label; 
Stuff2(); 

Label: 
Stuff3(); 

// Becomes: 

Stuff1); 
if (!cond) 
{ 
    Stuff2(); 
} 
Stuff3(); 

// CASE 2 

Stuff1(); 
Label: 
Stuff2(); 
Stuff3(); 
if (cond) goto Label; 

// becomes: 

Stuff1(); 
do 
{ 
    Stuff2(); 
    Stuff3(); 
} while (cond); 

и основывается на том, что для проверки каждого сложного случая и применять итерационные преобразования, которые приводят к этим тривиальным случаям. Затем он заканчивается окончательным алгоритмом устранения атак/меток.

Это очень интересное чтение.

UPDATE: Некоторые другие интересные статьи по этой теме (не легко получить ваши руки, поэтому я копирую прямые ссылки здесь для справки):

A Formal Basis for Removing Goto Statements

A Goto-Elimination Method And Its Implementation For The McCat C Compiler

+1

Спасибо, это по строкам, которые я искал! Я прочитаю документ и посмотрю, отвечает ли это на мой вопрос. – atlaste

+0

Patrice, статья - отличное чтение и, безусловно, очень хороший шаг к решению, которое я ищу; в любом случае он изящный и отвечает на мой вопрос. Я должен буду это проверить; Я не совсем уверен, какой код он будет производить (думаю, f.ex. о логических операторах), и задаться вопросом, нужны ли еще больше оптимизации АСТ, такие как вложение (копирование) фрагментов кода, обертывание фрагментов код в вспомогательных функциях и т. д. В любом случае это определенно стоит реализовать. Вы случайно не знаете, есть ли что-нибудь в этом отношении? – atlaste

+0

Мне нравится бумага. Интересно, что будет сделано с помощью устройства Даффа (http://en.wikipedia.org/wiki/Duff%27s_device). –

2

У меня есть практический опыт попыток взять неструктурированную программу (в COBOL, не менее) и сделать ее структурированной путем удаления всех экземпляров GOTO. Первоначальный программист был реформированным программистом Ассамблеи, и хотя он, возможно, знал о заявлении PERFORM, он не использовал его. Это был ГОТО ГОТО ГОТО. И это был серьезный код спагетти - несколько сотен строк кода спагетти. Я потратил пару недель на свободное время, пытаясь переписать эту чудовищную конструкцию, и в итоге мне пришлось сдаться. Это была огромная дымящая куча безумия. Это сработало, хотя! Его задачей было разобрать пользовательские инструкции, отправленные в мейнфрейм в текстовом формате, и все было хорошо.

Таким образом, НЕТ, не всегда возможно полностью исключить GOTO - если вы используете ручные методы. Это краевой случай, однако - существующий код, написанный человеком с явно искаженным программированием.В наше время есть инструменты, которые могут решить ранее неразрешимые структурные проблемы.

С этого дня я закодирован в Modula-2, C, Revelation Basic, трех вариантах VB и C# и никогда не нашел ситуации, которая требовала или даже предлагала GOTO в качестве решения. Однако для оригинала BASIC GOTO был неизбежен.

+0

Спасибо за конструктивную реакцию. То же самое касается меня; Я написал COMX BASIC в 1987 году и просто не имеет никаких функций, звонков и т. Д. - единственной альтернативой является чрезмерное использование спагетти GOTO. :-) После этого я редко их использовал (Open Addressing - одно из немногих исключений). Тем не менее, это говорит о том, что где-то в моем сознании есть алгоритм «алгоритма удаления goto» - то, что я пытаюсь сделать здесь, - это формализовать этот «алгоритм». – atlaste

+0

@Stefan Я предполагаю, что если бы кто-то перекодировал что-то с того момента, то в Basic один все равно сделал бы это по-другому. Как и мой стиль программирования C изменился после изучения C++, устремляясь к OO бедного человека: один мог бы управлять структурами бедного человека. –

+0

@PeterSchneider Я просто не думаю, что это всего лишь вопрос опыта - если я могу переписать goto-заявления как шаблоны OO, я тоже счастлив. Мой личный опыт заключается в том, что вы изучаете «шаблоны кода», которые решают конкретные проблемы, выраженные как «лучшая практика» на определенном языке. Если вы переводите фрагмент кода, вы пытаетесь вывести проблему, которая была решена, и «вставить» шаблон решения. В старые времена большинство этих образцов включали в себя тонны «goto» заявлений; вопрос заключается в следующем: как мы можем формализовать это и применим ли это решение к каждому утверждению goto, которое есть там? – atlaste

1

С вами сказал, что это теоретический вопрос, вот теоретический ответ.

Java - это Тьюринг завершен, так что, конечно. Вы можете выразить любую C# -программу в Java. Вы также можете выразить это в Minecraft Redstone или Minesweeper. По сравнению с этими альтернативами, выражающими это на Java, должно быть легко.

Очевидно, что более практичный ответ, а именно, понятный алгоритм для трансформации, был дан Патрисом.

1

ситуация, когда Гото можно избежать, но я думаю, что лучше использовать:

Когда мне нужно выйти из внутреннего цикла и цикла:

for(int i = 0; i < list.Count; i++) 
{ 
    // some code that initializes inner 
    for(int j = 0; j < inner.Count; j++) 
    { 
     // Some code. 
     if (condition) goto Finished; 
    } 
} 
Finished: 
// Some more code. 

Чтобы избежать Гото вы должны сделать что-то вроде этого:

for(int i = 0; i < list.Count; i++) 
{ 
    // some code that initializes inner 
    bool conditon = false; 
    for(int j = 0; j < inner.Count; j++) 
    { 
     // Some code that might change condition 
     if (condition) break; 
    } 
    if (condition) break; 
} 
// Some more code. 

Я думаю, что это выглядит намного лучше с утверждением goto.

Вторая ситуация в порядке, если внутренний цикл был в другом методе.

void OuterLoop(list) 
{ 
    for(int i = 0; i < list.Count; i++) 
    { 
     // some code that initializes inner 
     if (InnerLoop(inner)) break; 
    } 
} 
bool InnerLoop(inner) 
{ 
    for(int j = 0; j < inner.Count; j++) 
    { 
     // Some code that might change condition 
     if (condition) return true; 
    } 
    return false; 
} 
+0

Мне нравится ваше сообщение, но текущая версия 'InnerLoop()' всегда возвращает значение false, которое не соответствует его цели. Вы можете всегда возвращать 'condition' (и объявлять amd инициализировать его в false в' InnerLoop() '). –

+0

О, исправил это, надеюсь, что у вас все получилось. – Casperah

1

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

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

Вы обнаружите, что существуют большинство конструкций компилятора для обобщения конкретного использования goto, тех конструкций, где создается на основе общих шаблонов использования goto, существовавших до него. В статье Патриса Гахиде упоминаются такие шоу, которые проходят в обратном порядке. Если вы находите goto в своем коде, вы либо смотрите на один из этих шаблонов, и в этом случае вы должны заменить его на соответствующую конструкцию языка. Или вы ищете по существу неструктурированный код, в этом случае вы должны фактически структурировать код (или оставить его в покое). Изменение его на что-то неструктурированное, но без goto может только ухудшить ситуацию. (Тот же процесс обобщения все еще продолжается, рассмотрим, как foreach добавляется к компиляторам для обобщения очень распространенного использования for ...)