2014-09-16 11 views
4

goto 's로 만드는 것은 쉽습니다 (f.ex.IL에서 입증 됨). 모두 goto 문에 더 높은 수준의 표현식과 명령문 - 말하자면 - 지원되는 모든 것을 사용하십시오. Java에서.항상 고토를 제거 할 수 있습니까?

또는 원하는 경우 : 내가 찾고있는 것은 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를 감 쌉니다.

그럼 더 나은 해결책이 있습니까?


업데이트 1

많은 사람들이 질문은 '너무 광범위'라고 생각하는 것 때문에

, 나는 ... 좀 더 정교하게 자바 때문입니다 제가 말씀 드린 이유를 갈거야 Java에는 'goto'문이 없습니다. 내 취미 프로젝트 중 하나로서 C# 코드를 Java로 변환하려고 시도하고있었습니다. Java에서이 제한으로 인해 부분적으로 어려운 것으로 입증되었습니다.

저도 그렇게 생각했습니다. f.ex.가 있으면. 오픈 어드레싱 (참고 : http://en.wikipedia.org/wiki/Open_addressing - 참고 1 참조)에서 'remove'메소드의 구현은 예외적 인 경우에 'goto'를 사용하는 것이 편리하지만이 경우에는 'state'변수를 사용하여 다시 작성할 수 있습니다 . 이것은 하나의 예에 불과하며, 디 컴파일을 시도 할 때 톤과 톤의 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은 C 프로그램에서 모든 goto 문을 근절하기위한 알고리즘을 제안합니다. 이 방법은 C#으로 작성된 프로그램이나 if/switch/loop/break/continue (AFAIK와 같은 공통 구문을 사용하는 언어)에 적용 할 수 있습니다.하지만 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); 

를 그 각 복잡한 경우를 살펴 그 사소한 경우 발생할 반복 변형을 적용하는 발판으로

이 둘 간단한 변환을 시작한다. 그런 다음 궁극적 인 gotos/labels 소거 알고리즘으로 마무리합니다.

이것은 매우 흥미로운 내용입니다.

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 논리 연산자에 대한) 생각하지 않으며, 코드 조각을 인라인 (copy'ing) 조각과 같은 많은 AST 최적화가 필요한지 궁금해. 코드 도우미 함수, 등등. 어느 쪽이든 그것은 확실히 구현할 가치가있어. 그 위에 뭔가가 있는지 아시나요? – atlaste

+0

나는 종이를 좋아한다. Duff 's Device (http://en.wikipedia.org/wiki/Duff%27s_device)의 변형이 무엇인지 궁금합니다. –

2

구조화되지 않은 프로그램을 COBOL로 가져 와서 GOTO의 모든 인스턴스를 제거함으로써 구조화 된 것으로 렌더링하려는 실무 경험이 있습니다. 원래 프로그래머는 개혁 된 어셈블리 프로그래머였으며 PERFORM 성명을 알았지 만 그는 사용하지 않았습니다. 고토 고토 고토였습니다. 스파게티 코드가 수백 줄에 달하는 심각한 스파게티 코드였습니다. 나는이 무서운 구조물을 다시 쓰려고 노력하면서 2 주 정도의 여가 시간을 보냈다. 결국 나는 포기해야만했다. 그것은 광기의 거대한 김이 넘쳤습니다. 그래도 성공했습니다! 이 작업은 텍스트 형식으로 메인 프레임으로 전송 된 사용자 지침을 구문 분석하는 것이 었습니다.

수동 방법을 사용하는 경우 NO, 완전히 GOTO를 제거 할 수있는 것은 아닙니다. 그러나 이것은 최후의 경우이지만, 명백히 뒤틀린 프로그래밍 정신을 가진 사람이 작성한 기존 코드입니다.현대에는 다루기 힘든 구조적 문제를 해결할 수있는 도구가 있습니다.

그 날부터 Modula-2, C, Revelation Basic, VB의 세 가지 맛 및 C#을 코딩했으며 해결책으로 GOTO를 요구하거나 제안한 상황을 결코 발견하지 못했습니다. 그러나 원래 BASIC의 경우 GOTO는 피할 수 없었습니다.

+0

건설적인 반응 주셔서 감사. 저도 마찬가지입니다. 필자는 1987 년에 COMX BASIC을 작성했는데 기능, 통화 등이 없기 때문에 유일한 대안은 스파게티 GOTO를 과도하게 사용하는 것입니다. :-) 그 후 거의 사용하지 않았습니다 (Open Addressing은 몇 가지 예외 중 하나입니다). 여전히, 그것은 내 마음 속에 어딘가에 '고토 제거 알고리즘'이 있음을 암시합니다. 여기서 제가하려고하는 것은 그 '알고리즘'을 형식화하는 것입니다. – atlaste

+0

@Stefan 만약 내가 처음부터 다시 뭔가를 다시 코딩하는 사람은 여전히 ​​다르게 할 것이라고 가정합니다. 내 C 프로그래밍 스타일이 C++을 배우고 나서 바뀌었고, 가난한 사람의 OO로 돌보는 것처럼 : 사람은 가난한 사람의 통제 구조를 수행 할 것입니다. –

+0

@PeterSchneider 단지 경험 문제는 아니라고 생각합니다. goto 문을 객체 지향 패턴으로 다시 작성할 수 있으면 행복합니다. 저의 개인적인 경험은 특정 언어로 '모범 사례'로 표현되는 특정 문제를 해결하는 '코드 패턴'을 배우는 것입니다. 코드 조각을 번역하면 해결 된 문제를 추론하고 솔루션 템플릿을 '삽입'합니다. 예전에는 이러한 패턴의 대부분이 'goto'문으로 이루어졌습니다. 질문은 다음과 같습니다 : 우리는 이것을 어떻게 형식화 할 수 있습니까? 그리고 그 해결책은 거기에있는 모든 goto 문에 적용됩니까? – atlaste

1

당신 이후 이론적 인 질문입니다. 여기 이론적 인 대답이 있습니다.

Java는 물론 튜링이 완전하므로 물론 가능합니다. Java로 C# 프로그램을 표현할 수 있습니다. Minecraft Redstone이나 Minesweeper에서도 표현할 수 있습니다. 이러한 대안에 비해 Java로 표현하는 것이 쉬워야합니다.

분명 더 현실적인 대답, 즉 변환을 수행 할 수있는 알기 쉬운 알고리즘이 Patrice에 의해 제공되었습니다.

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'을 반환하고 (InnerLoop()에서 amd를 false로 초기화하도록 선언 할 수 있습니다). –

+0

오, 고쳐주었습니다. 어쨌든 요점을 얻었 으면합니다. – Casperah

1

나는 내 솔루션 단일 비트 좋아하지 않는다. 하나는, 그것은 추악한 죽은이고, 둘째, 그것은 기본적으로 goto가하는 것과 똑같은 스위치로 goto를 감 쌉니다.

goto의 용도를 대체 할 수있는 일반적인 패턴을 찾을 때 항상 얻으려고하는 것입니다. goto와 완전히 동일합니다. 그 밖의 무엇이있을 수 있 었는가? goto의 다른 용도는 가장 일치하는 언어 구조로 대체되어야합니다.이것이 바로 switch 문과 for 루프와 같은 구조가 처음부터 존재하여 그러한 프로그램 흐름을 생성하는 것이 쉽고 오류가 적어지는 이유입니다. 컴파일러는 여전히 고토 (또는 점프)를 생성하지만, 우리가 망칠 곳에서 일관되게 그렇게 할 것입니다. 그 위에 컴파일러가 생성하는 것을 읽을 필요는 없지만 더 쉽게 이해할 수있는 것을 읽게됩니다.

대부분의 컴파일러 구조는 goto의 특정 용도를 일반화하기 위해 존재하며, 그 구조는 이전에 존재했던 goto 사용 패턴의 공통 패턴을 기반으로 작성됩니다. Patrice Gahide 지에는 역순으로 진행되는 쇼들에 대한 언급이 있습니다. 코드에서 goto를 찾으면 해당 패턴 중 하나를보고 있습니다.이 경우 일치하는 언어 구문으로 바꿔야합니다. 또는 본질적으로 구조화되지 않은 코드를보고있는 경우 실제로 코드를 구조화해야합니다 (또는 그대로 두어야합니다). 구조가 바뀌지 만 무언가로 변경하면 goto 없이는 사태가 악화 될 수 있습니다. (일반적인 일반화 과정은 아직 진행 중입니다. foreach을 컴파일러에 추가하여 매우 일반적인 용도로 사용하기 위해 for ...