함수에 하나의 리턴 문 만 있어야합니까?


780

함수에 하나의 return 문만있는 것이 더 좋은 이유는 무엇입니까?

또는 논리적으로 정확하자마자 함수에서 반환해도됩니다. 즉, 함수에 많은 return 문이있을 수 있습니까?


25
질문이 언어에 구애받지 않는다는 것에 동의하지 않습니다. 일부 언어의 경우 여러 수익을 얻는 것이 다른 언어보다 자연스럽고 편리합니다. RAII를 사용하는 C ++보다 C 함수의 초기 반환에 대해 불평 할 가능성이 큽니다.
Adrian McCarthy

3
이것은 밀접하게 관련되어 있으며 훌륭한 답변이 있습니다 : programmers.stackexchange.com/questions/118703/…
Tim Schmelter

언어에 구애받지 않습니까? 함수형 언어를 사용하는 사람에게 함수 당 하나의 리턴을 사용해야한다고 설명하십시오 .p
Boiethios

답변:


741

나는 종종 "쉬운"상황으로 돌아 가기 위해 메소드를 시작할 때 몇 가지 진술을한다. 예를 들면 다음과 같습니다.

public void DoStuff(Foo foo)
{
    if (foo != null)
    {
        ...
    }
}

... 다음과 같이 더 읽기 쉽게 (IMHO) 만들 수 있습니다.

public void DoStuff(Foo foo)
{
    if (foo == null) return;

    ...
}

그렇습니다. 함수 / 메소드에서 여러 "종료점"을 갖는 것이 좋습니다.


83
동의했다. 여러 개의 종료 점이 있으면 손을 can 수 있지만 전체 기능을 IF 블록에 배치하는 것보다 낫습니다. 코드를 읽을 수있게 유지하는 것이 합리적으로 자주 return을 사용하십시오.
Joshua Carmody

172
이것은 "guard statement"로 알려져 있으며 Fowler 's Refactoring입니다.
Lars Westergren

12
함수가 비교적 짧게 유지되는 경우, 리턴 포인트가 중간 부근에있는 함수의 구조를 따르는 것은 어렵지 않습니다.
KJAWolf 2016 년

21
if-else 문장의 거대한 블록, 각각은 반환? 그건 아무것도 아닙니다. 이러한 것은 일반적으로 쉽게 리팩토링됩니다. (결과 변수가있는 가장 일반적인 단일 출구 변형은 가장 흔하므로 다중 출구 변형이 더 어려울 것입니다.) 실제 두통을 원한다면 if-else와 while 루프의 조합을 살펴보십시오 (로컬에 의해 제어 됨) booleans), 결과 변수가 설정되어 메소드 종료시 최종 종료 점이 발생합니다. 그것은 하나의 출구 아이디어가 화를 냈고, 그렇습니다.
Marcus Andrén

7
'상상 :'DoStuff '기능의 끝에서 "IncreaseStuffCallCounter"메소드를 수행해야합니다. 이 경우 어떻게 하시겠습니까? :) '-DoStuff() { DoStuffInner(); IncreaseStuffCallCounter(); }
짐 Balter

355

아무도 언급하거나 인용하지 않았습니다 Code Complete를 하지 않았으므로 그렇게 할 것입니다.

17.1 귀국

각 루틴의 수익 수를 최소화 . 맨 아래에서 루틴을 읽고 위의 어딘가에 리턴 될 가능성을 모르면 루틴을 이해하기가 더 어렵습니다.

가독성을 높이려면 리턴을 사용하십시오 . 특정 루틴에서 응답을 알고 나면 즉시 호출 루틴으로 리턴하려고합니다. 루틴이 정리가 필요없는 방식으로 정의 된 경우 즉시 리턴하지 않으면 더 많은 코드를 작성해야합니다.


64
"최소화"의 뉘앙스에 대해 +1이지만 다중 수익을 금지하지는 않습니다.
Raedwald

13
"이해하기 어렵다"는 저자가 일반적인 주장을 뒷받침 할 경험적 증거를 제공하지 않을 때 매우 주관적이다. 최종 반환 진술을위한 코드에서 많은 조건부 위치에 설정되어야하는 단일 변수를 갖는 것은 동등하게 적용된다 "변수가 그 함수의 위 어딘가에 할당되었을 가능성을 인식하지 못합니다!
Heston T. Holtmann

26
개인적으로, 가능한 빨리 일찍 돌아 오는 것을 선호합니다. 왜? 특정 사례에 대한 리턴 키워드가 표시되면 즉시 "완료되었습니다"라는 사실을 알게됩니다. 나중에 어떤 일이 발생하는지 파악하기 위해 계속 읽을 필요는 없습니다.
마크 심슨

12
@ HestonT.Holtmann : 프로그래밍 북에서 Code Complete를 독특하게 만든 것은 조언 경험적 증거에 의해 뒷받침되었다는 것입니다.
Adrian McCarthy

9
여러 개의 리턴 포인트를 갖는 것이 항상 좋은 것은 아니지만 때로는 필요하다는 점을 언급하기 때문에 아마도 받아 들일만한 대답이어야합니다.
Rafid

229

기술이 실제로 반복 해서 유용하다는 것을 알았으므로 여러 종료점에 대해 임의로 결정하는 것은 엄청나게 현명하지 않을 것이라고 말할 것입니다 . 실제로 는 명확성을 위해 기존 코드 를 여러 개의 종료점으로 리팩토링했습니다 . 따라서 두 가지 접근법을 비교할 수 있습니다.

string fooBar(string s, int? i) {
  string ret = "";
  if(!string.IsNullOrEmpty(s) && i != null) {
    var res = someFunction(s, i);

    bool passed = true;
    foreach(var r in res) {
      if(!r.Passed) {
        passed = false;
        break;
      }
    }

    if(passed) {
      // Rest of code...
    }
  }

  return ret;
}

여러 종료 지점 코드이 비교 된다 허용 : -

string fooBar(string s, int? i) {
  var ret = "";
  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

후자는 상당히 명확하다고 생각합니다. 내가 여러 출구 지점에 대한 비판을 말할 수있는 한 요즘은 다소 고전적인 관점입니다.


12
선명도는 보는 사람의 눈에 있습니다. 나는 기능을 살펴보고 중간과 끝을 찾는다. 기능이 작을 때-왜 무언가가 깨져서 "코드의 남음"이 중요하지 않은지 밝혀 내려고 할 때 ret이 왜 그런지 찾는 나이를 보낼 수 있습니다.
Murph

7
우선, 이것은 고안된 예입니다. 둘째, : string ret; "은 두 번째 버전으로 이동합니까? 셋째, Ret에는 유용한 정보가 포함되어 있지 않습니다. 넷째, 왜 하나의 함수 / 메서드에 너무 많은 논리가 있습니까? 다섯째, DidValuesPass (type res)와 RestOfCode ()를 구분하지 않는 이유는 무엇입니까? 하위 기능?
Rick Minerich

25
@Rick 1. 내 경험에 의지하지 않고, 이것은 실제로 여러 번 나온 패턴입니다. 2. '코드 나머지'에 할당되어 있습니다. 어쩌면 분명하지 않습니다. 3. 음? 예입니까? 4. 글쎄요,이 점에서이 점에
의거 한

5
@Rick 요점은 거대한 if 문으로 코드를 마무리하는 것보다 일찍 반환하는 것이 더 쉽다는 것입니다. 적절한 리팩토링으로도 내 경험에 많이 등장합니다.
ljs

5
여러 개의 return 문을 사용하지 않는 점은 디버깅을보다 쉽게 ​​만드는 것입니다. 끝에 하나의 중단 점이 있으면 종료 값을 볼 수 있습니다 (예외 예외). 가독성과 관련하여 2 개의 표시된 기능은 동일하게 작동하지 않습니다. ! r.Passed 경우 "더 읽기 쉬운"문자열이이를 반환하여 null을 반환하면 기본 반환 값은 빈 문자열입니다. 저자는 초기에 단 몇 줄 만에 기본값이 있음을 잘못 읽었습니다. 사소한 예에서도 불명확 한 기본 수익을 얻는 것은 쉽습니다. 마지막에 한 번의 수익이 시행되는 데 도움이됩니다.
Mitch

191

저는 현재 코드베이스에서 작업하는 두 사람이 "단일 종료점"이론을 맹목적으로 구독하는 코드베이스를 작업 중이며 경험을 통해 끔찍한 끔찍한 사례라고 말할 수 있습니다. 코드를 유지 관리하기가 매우 어려워서 그 이유를 보여 드리겠습니다.

"단일 종료점"이론을 사용하면 필연적으로 다음과 같은 코드가 생깁니다.

function()
{
    HRESULT error = S_OK;

    if(SUCCEEDED(Operation1()))
    {
        if(SUCCEEDED(Operation2()))
        {
            if(SUCCEEDED(Operation3()))
            {
                if(SUCCEEDED(Operation4()))
                {
                }
                else
                {
                    error = OPERATION4FAILED;
                }
            }
            else
            {
                error = OPERATION3FAILED;
            }
        }
        else
        {
            error = OPERATION2FAILED;
        }
    }
    else
    {
        error = OPERATION1FAILED;
    }

    return error;
}

이렇게하면 코드를 따르기가 매우 어려울뿐만 아니라 나중에 다시 돌아가서 1과 2 사이의 작업을 추가해야한다고 말하십시오. 전체 괴물 기능에 대해 들여 쓰기해야하며 행운을 빌어야합니다. if / else 조건과 괄호가 올바르게 일치하는지 확인하십시오.

이 방법을 사용하면 코드 유지 관리가 매우 어렵고 오류가 발생하기 쉽습니다.


5
@ 머프 : 당신은 각 조건을 읽지 않고 각 조건 후에 다른 것이 발생하지 않는다는 것을 알 방법이 없습니다. 일반적으로 이런 종류의 주제는 주관적이라고 말하지만 이것은 분명히 틀린 것입니다. 각 오류에 대한 답장으로, 당신은 끝났고, 당신은 정확히 무슨 일이 있었는지 알 수 있습니다.
GEOCHET

6
@ 머프 : 나는 이런 종류의 코드가 남용되고 남용되는 것을 보았습니다. 내부에 true if / else가 없으므로 예제는 매우 간단합니다. 이런 종류의 코드를 분해해야하는 것은 잊어 버린 "다른 것"입니다. AFAIK,이 코드는 실제로 예외가 필요합니다.
paercebal

15
이것으로 리팩토링하고 "순도"를 유지합니다 : if (! SUCCEEDED (Operation1 ())) {} else error = OPERATION1FAILED; if (error! = S_OK) {if (SUCCEEDED (Operation2 ())) {} else error = OPERATION2FAILED; } if (error! = S_OK) {if (SUCCEEDED (Operation3 ())) {} else error = OPERATION3FAILED; } // 등.
Joe Pineda

6
이 코드에는 하나의 종료 점이 없습니다. 각 "error ="문은 종료 경로에 있습니다. 함수를 종료하는 것이 아니라 블록이나 시퀀스를 종료하는 것입니다.
Jay Bazuzi

6
단일 반환이 "필연적으로"깊은 중첩을 초래한다는 데 동의하지 않습니다. 예는 단일 반환 (gotos없이)으로 간단한 선형 함수로 작성할 수 있습니다. 또한 리소스 관리를 위해 RAII를 독점적으로 사용하지 않거나 의존 할 수없는 경우 조기 반환으로 인해 누수가 발생하거나 코드가 중복됩니다. 가장 중요한 것은 조기 반품으로 인해 사후 조건을 주장하는 것이 비현실적이라는 것입니다.
Adrian McCarthy

72

구조화 된 프로그래밍에 따르면 함수 당 하나의 return 문만 있어야합니다. 이것은 복잡성을 제한하기위한 것입니다. Martin Fowler와 같은 많은 사람들은 여러 개의 return 문으로 함수를 작성하는 것이 더 간단하다고 주장합니다. 그는 자신이 쓴 고전적인 리팩토링 책 에서이 주장을 제시한다 . 다른 조언을 따르고 작은 기능을 작성하면 잘 작동합니다. 나는이 관점에 동의하며 엄격한 구조적 프로그래밍 순수 주의자 만이 함수 당 단일 리턴 문을 준수합니다.


44
구조화 된 프로그래밍은 아무 말도하지 않습니다. 구조화 된 프로그래밍의 옹호자로 자신을 묘사하는 일부 (모두는 아님) 사람들이 말합니다.
nono

15
"이것은 다른 조언을 따르고 작은 기능을 작성하면 잘 작동합니다." 이것이 가장 중요한 포인트입니다. 작은 함수에는 거의 많은 리턴 포인트가 필요하지 않습니다.

6
주석은 @wnoise +1이므로 사실입니다. 모든 "구조적 프로그래밍"은 GOTO를 사용하지 않는다고 말합니다.
paxos1977

1
@ceretullis : 꼭 필요한 것이 아니라면. 물론 필수는 아니지만 C에서는 유용 할 수 있습니다. 리눅스 커널은 그것을 사용합니다. GOTO는 해머 풀이 GOTO기능이 존재하더라도 제어 흐름을 이동 하는 데 사용 하는 것에 대해 이야기 했습니다. "사용하지 마십시오 GOTO" 라고 절대 말하지 않습니다 .
Esteban Küber

1
"코드의 '구조'를 무시하는 것입니다."-아니요, 정반대입니다. "피해야한다고 말하는 것이 합리적이다"-아니, 그렇지 않다.
Jim Balter

62

Kent Beck이 구현 패턴 에서 가드 절을 논의 할 때 루틴을 만드는 것은 단일 진입 점과 종료점을 갖습니다 ...

"동일한 루틴으로 여러 위치로 들어오고 나갈 때 혼동을 피할 수있었습니다. FORTRAN 또는 많은 글로벌 데이터로 작성된 어셈블리 언어 프로그램에 적용 할 때 어떤 명령문이 실행되었는지 이해하는 것이 어려운 경우에 적합했습니다. 작은 방법과 대부분의 로컬 데이터 만 있으면 불필요하게 보수적입니다. "

하나의 긴 중첩 if then else문 보다 가드 절로 작성된 함수를 훨씬 쉽게 찾을 수 있습니다.


물론 "가장 긴 if-then-else 문 중 하나"가 guard 절의 유일한 대안은 아닙니다.
Adrian McCarthy

@AdrianMcCarthy 더 나은 대안이 있습니까? 풍자보다 더 유용 할 것입니다.
shinzou

@ kuhaku : 나는 그 풍자에 전화 확실하지 않습니다. 대답은 가드 절 또는 긴 중첩 된 if-then-else 중 하나 또는 상황이라는 것을 나타냅니다. 많은 (가장?) 프로그래밍 언어는 보호 조항 외에 이러한 논리를 고려할 수있는 많은 방법을 제공합니다.
Adrian McCarthy

61

부작용이없는 함수에서는 단일 리턴 이상을 가질만한 이유가 없으며 기능적인 스타일로 작성해야합니다. 부작용이있는 방법에서는 상황이보다 순차적 (시간 색인화 됨)되므로 return 문을 명령으로 사용하여 명령을 실행 중지 명령으로 작성합니다.

즉, 가능하면이 스타일을 선호하십시오.

return a > 0 ?
  positively(a):
  negatively(a);

이 위에

if (a > 0)
  return positively(a);
else
  return negatively(a);

여러 계층의 중첩 조건을 작성하는 경우 술어 목록을 사용하여 리팩토링 할 수있는 방법이있을 수 있습니다. if와 else가 구문 적으로 멀리 떨어져 있음을 발견하면 더 작은 함수로 세분화 할 수 있습니다. 한 화면 이상의 텍스트에 걸쳐있는 조건부 블록은 읽기가 어렵습니다.

모든 언어에 적용되는 단단하고 빠른 규칙은 없습니다. 단일 return 문을 사용하는 것만으로는 코드가 좋지 않습니다. 그러나 좋은 코드는 그런 식으로 함수를 작성하는 경향이 있습니다.


6
+1 "만약 당신의 if와 다른 것들이 구문 상으로 멀리 떨어져 있다면, 그것을 더 작은 함수로 나누고 싶을 것입니다."
Andres Jaan Tack

4
+1, 이것이 문제라면 일반적으로 단일 기능으로 너무 많은 일을하고 있음을 의미합니다. 이 답변이 가장 높은 투표 답변이 아님을 정말 우울하게
여깁니다

1
가드 선언문에는 부작용이 없지만 대부분의 사람들은 유용하다고 생각합니다. 따라서 부작용이 없더라도 실행을 조기에 중지해야하는 이유가있을 수 있습니다. 이 답변은 내 의견으로는 문제를 완전히 해결하지 못합니다.
Maarten Bodewes

@ MaartenBodewes-owlstead "엄격함과 게으름"참조
Apocalisp

43

RAII 또는 다른 자동 메모리 관리가없는 경우 각 반품마다 정리해야합니다. 정리 또는 goto (논리적으로 관리되는 언어에서 '최종'과 동일)의 둘 다 잘못된 형식으로 간주됩니다. C ++ 또는 다른 자동 메모리 시스템에서 스마트 포인터 및 컬렉션을 사용하는 경우에는 그럴만 한 이유가 없으며 가독성과 더 많은 판단이 필요합니다.


잘 말했지만, 고도로 최적화 된 코드 (예 : 소프트웨어 스키닝 복잡한 3D 메쉬)를 작성하려고 할 때 삭제를 복사하는 것이 좋습니다.
Grant Peters

1
왜 그렇게 믿습니까? 를 역 참조하는 데 약간의 오버 헤드가있는 최적화가 불량한 컴파일러가있는 auto_ptr경우 일반 포인터를 병렬로 사용할 수 있습니다. 처음에는 최적화되지 않은 컴파일러로 '최적화 된'코드를 작성하는 것이 이상 할 것입니다.
Pete Kirkham

프로그래밍 언어에 메소드 끝에서 자동으로 호출되는 것이 포함되어 있지 않고 (예 : Java에서 try... finally) 자원 유지 보수를 수행해야하는 경우 단일로 수행 할 수 있습니다. 메소드의 끝에서 리턴 이를 수행하기 전에 상황을 제거하기 위해 코드 리팩토링을 신중하게 고려해야합니다.
Maarten Bodewes

@PeteKirkham 왜 정리를 위해 goto가 나쁜가요? 예 goto를 잘못 사용할 수 있지만이 특정 사용법은 나쁘지 않습니다.
q126y

1
C ++의 @ q126y는 RAII와 달리 예외가 발생하면 실패합니다. C에서는 완벽하게 유효한 방법입니다. stackoverflow.com/questions/379172/use-goto-or-not를
Pete Kirkham

40

함수 중간 에 return 문 이 잘못 되었다는 생각 에 의존합니다. 리턴을 사용하여 함수 상단에 몇 가지 가드 절을 작성할 수 있으며 물론 컴파일러에게 문제없이 함수 끝에서 리턴 할 항목을 알려주지 만 함수 중간 에 리턴하는 것은 놓치기 쉬울 수 있습니다. 함수를 해석하기 어렵게 만듭니다.


38

함수에 하나의 return 문만있는 것이 더 좋은 이유는 무엇입니까?

, 있습니다 :

  • 단일 출구 지점은 사후 조건을 주장 할 수있는 훌륭한 장소입니다.
  • 함수 끝에서 하나의 리턴에 디버거 중단 점을 둘 수있는 것이 종종 유용합니다.
  • 반품이 적을수록 복잡성이 줄어 듭니다. 선형 코드는 일반적으로 이해하기 쉽습니다.
  • 함수를 단일 리턴으로 단순화하려고하면 복잡성이 발생하면 더 작고 더 일반적이며 이해하기 쉬운 함수로 리팩토링하는 것이 좋습니다.
  • 소멸자가없는 언어이거나 RAII를 사용하지 않는 경우 한 번의 반환으로 정리해야 할 장소의 수가 줄어 듭니다.
  • 일부 언어에는 단일 종료 점이 필요합니다 (예 : Pascal 및 Eiffel).

이 질문은 종종 여러 반환 또는 깊은 중첩 if 문 사이의 잘못된 이분법으로 제기됩니다. 거의 항상 하나의 출구 지점으로 매우 선형 (깊은 중첩 없음) 인 세 번째 솔루션이 있습니다.

업데이트 : 분명히 MISRA 지침은 단일 출구를 촉진합니다 합니다.

분명히하기 위해, 여러 번의 수익을 얻는 것이 항상 잘못되었다는 말은 아닙니다 . 그러나 동등한 솔루션이 주어지면 단일 수익을 가진 솔루션을 선호하는 데에는 많은 이유가 있습니다.


2
하나의 리턴 명령문을 갖는 또 다른 좋은 이유는 아마도 요즘 가장 좋은 것은 로깅입니다. 메소드에 로깅을 추가하려는 경우 메소드가 리턴하는 내용을 전달하는 단일 로그 명령문을 배치 할 수 있습니다.
inor

FORTRAN ENTRY 문은 얼마나 흔했습니까? docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn99/index.html을 참조하십시오 . 그리고 모험적이라면 AOP와 애프터 어드바이스로 메소드를 기록 할 수 있습니다
Eric Jablow

1
+1 처음 2 점은 저를 설득하기에 충분했습니다. 마지막 두 번째 단락에서도 마찬가지입니다. 다형성이 OOP에 도입 된 주된 이유 인 단일 책임 규칙을 위반하도록 장려하기 때문에 깊은 중첩 조건을 권장하지 않는 것과 같은 이유로 로깅 요소에 동의하지 않습니다.
Francis Rodgers

C # 및 Code Contracts를 사용 Contract.Ensures하여 여러 리턴 포인트와 함께 사용할 수 있기 때문에 사후 조건 문제는 문제가되지 않습니다 .
julealgon

1
@ q126y : goto일반적인 정리 코드를 얻는 데 사용 하는 경우 정리 코드 return끝에 하나가 있도록 함수를 단순화했을 것입니다 . 따라서 문제를 해결했다고 말할 수는 goto있지만을 하나로 단순화하여 문제를 해결했다고 말할 수 있습니다 return.
Adrian McCarthy

33

단일 종료점을 갖는 것은 함수의 끝에 단일 중단 점을 설정하여 실제로 리턴 될 값을 확인할 수 있기 때문에 디버깅에 유리합니다.


6
브라보! 이 객관적인 이유 를 언급 한 사람 은 귀하 뿐입니다 . 이것이 단일 출구 지점과 여러 출구 지점을 선호하는 이유입니다. 내 디버거가 중단 점 설정 수 있다면 어떤 출구 지점을 아마 여러 번 출구 포인트를 원합니다. 내 현재 의견은 여러 종료점을 코드로 작성하는 사람들이 코드에서 디버거를 사용해야하는 다른 사람들을 희생하여 자신의 이익을 위해 그것을 수행한다는 것입니다 (그렇습니다. 여러 개의 출구 지점.)
MikeSchinkel

3
예. 프로덕션에서 간헐적으로 오작동하는 시스템에 로깅 코드를 추가하고 있습니다 (스텝 할 수 없음). 이전 코더가 단일 종료를 사용했다면 훨씬 쉬웠을 것입니다.
Michael Blackburn

3
사실 디버깅에 도움이됩니다. 그러나 실제로 대부분의 경우 호출 직후 호출 함수에서 중단 점을 설정할 수 있었으며 사실상 동일한 결과를 얻었습니다. (그리고 그 위치는 물론 콜 스택에서 찾을 수 있습니다.) YMMV.
foo

디버거는 스텝 아웃 또는 단계 복귀 기능을 제공합니다 (각과마다 디버거가 내가 아는 않습니다) 반환 값이 잘되는 쇼,하지 않는 한이다 반환됩니다. 변수에 할당되지 않으면 나중에 값을 변경하는 것이 약간 까다로울 수 있습니다.
Maarten Bodewes

7
오랫동안 디버거를 보지 못했지만 메소드의 "닫기"(종료, 오른쪽 중괄호, 언어에 관계없이)에 중단 점을 설정하고 위치 또는 수에 관계없이 중단 점에 도달 할 수 없습니다 , 반환 상태는 메소드에 있습니다. 또한 함수에 반환 값이 하나만 있어도 예외 (명시 적 또는 상속 된)로 함수를 종료 할 수 없습니다. 그래서 이것이 실제로 유효한 포인트는 아니라고 생각합니다.
Scott Gartner

19

일반적으로 함수에서 하나의 종료점 만 갖려고합니다. 그러나 실제로 그렇게하면 필요한 것보다 더 복잡한 함수 본문이 만들어지는 경우가 있습니다.이 경우 여러 개의 종료점을 갖는 것이 좋습니다. 결과적으로 복잡성을 기반으로 한 "심사 요청"이어야하지만 목표는 복잡성과 이해도를 희생하지 않으면 서 가능한 적은 출구 지점이어야합니다.


"일반적으로 함수에서 하나의 종료점 만 가지려고합니다"-왜? "목표는 가능한 적은 출구 점이어야합니다"– 왜? 왜 19 명이이 답변에 투표하지 않았습니까?
Jim Balter

@JimBalter 궁극적으로 개인 취향으로 요약됩니다. 더 많은 출구 지점은 일반적으로 더 복잡한 방법 으로 이어지지 만 (항상 그런 것은 아니지만) 다른 사람이 이해하기 어렵게 만듭니다.
Scott Dorman

"그것은 개인적인 취향으로 귀결됩니다."-즉, 이유를 제시 할 수 없습니다. "종료 지점이 많을수록 더 복잡한 방법으로 이어집니다 (항상 그런 것은 아니지만)"-아니요, 실제로는 그렇지 않습니다. 논리적으로 동등한 두 개의 함수, 가드 절과 단일 종료를 갖는 두 가지 함수가 주어지면 후자는 순환 순환 복잡성이 높아져 많은 연구에서 오류가 발생하기 쉽고 이해하기 어려운 코드가 생성됩니다. 다른 답변을 읽으면 도움이 될 것입니다.
Jim Balter

14

아니요, 1970 년대에 더 이상 살지 않기 때문 입니다. 함수가 여러 리턴 값이 문제가 될만큼 충분히 길면 너무 깁니다.

(예외가있는 언어의 여러 줄 함수에는 여러 개의 종료 점이 있습니다.)


14

실제로 상황을 복잡하게하지 않는 한 단일 출구를 선호합니다. 경우에 따라 여러 개의 존재 지점이 다른 중요한 디자인 문제를 숨길 수 있음을 발견했습니다.

public void DoStuff(Foo foo)
{
    if (foo == null) return;
}

이 코드를 보면 즉시 다음과 같이 묻습니다.

  • 'foo'가 null입니까?
  • 그렇다면 몇 개의 'DoStuff'클라이언트가 널 'foo'로 함수를 호출 한 적이 있습니까?

이러한 질문에 대한 답변에 따라

  1. 수표는 사실이 아니므로 의미가 없습니다 (즉, 주장이어야 함)
  2. 확인은 매우 드물기 때문에 어쨌든 다른 조치를 취해야하므로 특정 호출자 기능을 변경하는 것이 좋습니다.

위의 두 경우 모두 'foo'가 null이 아니며 관련 호출자가 변경되도록하기 위해 어설 션으로 코드를 재 작업 할 수 있습니다.

여러 개가 존재하는 두 가지 다른 이유 (구체적으로 C ++ 코드로 생각)가 실제로 음수를 가질 수 있습니다 영향을 줄 수 있습니다. 그것들은 코드 크기와 컴파일러 최적화입니다.

함수 종료시 범위에있는 비 POD C ++ 객체에는 소멸자가 호출됩니다. 리턴 문이 여러 개인 경우 범위에 다른 오브젝트가있을 수 있으므로 호출 할 소멸자 목록이 달라집니다. 따라서 컴파일러는 각 return 문에 대한 코드를 생성해야합니다.

void foo (int i, int j) {
  A a;
  if (i > 0) {
     B b;
     return ;   // Call dtor for 'b' followed by 'a'
  }
  if (i == j) {
     C c;
     B b;
     return ;   // Call dtor for 'b', 'c' and then 'a'
  }
  return 'a'    // Call dtor for 'a'
}

코드 크기가 문제라면 피할 가치가 있습니다.

다른 문제는 "명명 된 반환 값 최적화"(일명 복사 제거, ISO C ++ '03 12.8 / 15)와 관련이 있습니다. C ++에서는 구현이 다음과 같은 경우 복사 생성자 호출을 건너 뛸 수 있습니다.

A foo () {
  A a1;
  // do something
  return a1;
}

void bar () {
  A a2 ( foo() );
}

코드를 그대로 사용하면 객체 'a1'은 'foo'로 구성되고 복사 구성은 'a2'를 구성하기 위해 호출됩니다. 그러나 복사 제거는 컴파일러가 스택에서 'a2'와 동일한 위치에 'a1'을 구성 할 수 있도록합니다. 따라서 함수가 반환 될 때 객체를 "복사"할 필요가 없습니다.

다중 종료점은이를 감지하는 데있어 컴파일러의 작업을 복잡하게 만들고, 상대적으로 최신 버전의 VC ++의 경우 함수 본문에 다중 리턴이있는 위치에서는 최적화가 수행되지 않았습니다. 자세한 내용 은 Visual C ++ 2005의 명명 된 반환 값 최적화 를 참조하십시오.


1
C ++ 예제에서 마지막 dtor를 제외한 나머지를 모두 가져 가면 if 문의 범위가 끝날 때 B 이상 C 및 B를 파괴하는 코드가 계속 생성되어야하므로 여러 반환 값이 없어서 아무것도 얻지 못합니다. .
Eclipse

4
+1 그리고 목록의 맨 아래에 우리는 이 코딩 방식이 존재하는 진짜 이유 인 NRVO를 가지고 있습니다. 그러나 이것은 미세 최적화입니다. 그리고 모든 마이크로 최적화 관행과 마찬가지로 아마도 300kHz PDP-8에서 프로그래밍하는 데 사용되는 50 세의 "전문가"에 의해 시작되었으며 깨끗하고 구조화 된 코드의 중요성을 이해하지 못합니다. 일반적으로 Chris S의 조언을 듣고 적절할 때마다 여러 개의 return 문을 사용하십시오.
BlueRaja-대니 Pflughoeft

귀하의 선호도에 동의하지 않지만 ( throw new ArgumentNullException()이 경우 C #에서 와 같이 Assert 제안도 반환 지점입니다 ) 다른 고려 사항이 마음에 들었습니다. 모두 나에게 유효하며 일부에서는 중요 할 수 있습니다 틈새 컨텍스트.
julealgon

이것은 밀짚 꾼으로 가득 차 있습니다. 왜 문제 foo테스트되고 어떻게해야하는지 여부입니다 주제와는 아무 상관이 없습니다 if (foo == NULL) return; dowork; 또는if (foo != NULL) { dowork; }
짐 Balter

11

종료 점이 하나 있으면 사이클로 매틱 복잡성이 감소 하므로 이론적 으로 코드를 변경할 때 버그가 발생할 가능성이 줄어 듭니다. 그러나 연습은보다 실용적인 접근 방식이 필요하다는 것을 암시하는 경향이 있습니다. 따라서 단일 종료점을 갖는 것을 목표로하지만 더 읽기 쉬운 코드는 여러 개 가질 수 있습니다.


매우 통찰력이 있습니다. 프로그래머가 여러 개의 종료점을 사용할시기를 알 때까지는 하나의 종료점으로 제한되어야한다고 생각합니다.
Rick Minerich

5
실제로는 아닙니다. "if (...) return; ... return;"의 순환 복잡성 "if (...) {...} return;"과 같습니다. 그들은 둘을 통해 두 가지 경로가 있습니다.
Steve Emmerson

11

return어떤 의미에서는 코드 냄새가 나기 때문에 하나의 문장 만 사용하도록 강요 합니다. 설명하겠습니다 :

function isCorrect($param1, $param2, $param3) {
    $toret = false;
    if ($param1 != $param2) {
        if ($param1 == ($param3 * 2)) {
            if ($param2 == ($param3 / 3)) {
                $toret = true;
            } else {
                $error = 'Error 3';
            }
        } else {
            $error = 'Error 2';
        }
    } else {
        $error = 'Error 1';
    }
    return $toret;
}

(조건은 영국식입니다 ...)

조건이 많을수록 함수가 커질수록 읽기가 더 어려워집니다. 따라서 코드 냄새에 익숙해지면이를 인식하고 코드를 리팩토링하려고합니다. 가능한 두 가지 솔루션은 다음과 같습니다.

  • 복수 반품
  • 별도의 기능으로 리팩토링

여러 반환

function isCorrect($param1, $param2, $param3) {
    if ($param1 == $param2)       { $error = 'Error 1'; return false; }
    if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
    if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
    return true;
}

별도의 기능

function isEqual($param1, $param2) {
    return $param1 == $param2;
}

function isDouble($param1, $param2) {
    return $param1 == ($param2 * 2);
}

function isThird($param1, $param2) {
    return $param1 == ($param2 / 3);
}

function isCorrect($param1, $param2, $param3) {
    return !isEqual($param1, $param2)
        && isDouble($param1, $param3)
        && isThird($param2, $param3);
}

물론 더 길고 약간 지저분하지만이 방법으로 함수를 리팩터링하는 과정에서

  • 재사용 가능한 많은 함수를 만들었습니다.
  • 함수를보다 인간이 읽을 수있게하고
  • 함수의 초점은 값이 올바른 이유에 있습니다.

5
-1 : 나쁜 예입니다. 오류 메시지 처리를 생략했습니다. 그것이 필요하지 않다면 isCorrect는 return xx && yy && zz; 여기서 xx, yy 및 z는 isEqual, isDouble 및 isThird 표현식입니다.
kauppi 2009

10

필요한만큼 또는 코드를 더 깨끗하게 만드는 것 (예 : guard 절) 이 있어야합니다. ) .

나는 개인적으로 "모범 사례"에 대해 한 번만 돌려주는 진술을 들어 본 적이 없다.

대부분의 경우 논리 경로를 기반으로 최대한 빨리 함수를 종료하는 경향이 있습니다 (guard 절은 이에 대한 훌륭한 예입니다).


10

나는 C #으로 작성한 코드에서 일반적으로 여러 반환이 좋다고 생각합니다. 단일 반환 스타일은 C의 보류 방식이지만 C로 코딩되지 않았을 수 있습니다.

모든 프로그래밍 언어에서 메소드에 대해 하나의 종료점 만 요구하는 법은 없습니다 . 어떤 사람들은이 스타일의 우월성을 주장하고 때로는 "규칙"이나 "법"으로 높이려고하지만이 믿음은 어떠한 증거 나 연구에 의해서도 뒷받침되지 않습니다.

자원을 명시 적으로 할당 해제해야하는 C 코드에서는 하나 이상의 리턴 스타일이 나쁜 습관 일 수 있지만 Java, C #, Python 또는 JavaScript와 같은 언어는 자동 가비지 콜렉션 및 try..finally블록 (및using C #의 블록) . ),이 주장은 적용되지 않습니다. 이러한 언어에서는 중앙 집중식 수동 리소스 할당 해제가 필요합니다.

단일 수익률이 더 읽기 쉬운 경우와 그렇지 않은 경우가 있습니다. 코드 줄 수를 줄이거 나 논리를 더 명확하게 만들거나 중괄호와 들여 쓰기 또는 임시 변수 수를 줄이십시오.

따라서 기술적 인 문제가 아닌 레이아웃 및 가독성 문제이기 때문에 예술적 감각에 맞는만큼의 수익을 사용하십시오.

내 블로그에서 이것에 대해 더 오랫동안 이야기했습니다 .


10

필연적으로 발생하는 "화살표" 프로그래밍 에 대해 나쁜 말이있는 것처럼 단일 종료점을 갖는 것에 대해 좋은 말이 있습니다.

입력 유효성 검사 또는 리소스 할당 중에 여러 개의 종료점을 사용하는 경우 모든 '오류 종료'를 함수의 맨 위에 눈에 띄게 배치하려고합니다.

"SSDSLPedia" 의 스파르타 프로그래밍 기사와 단일 기능 종료점 "Portland Pattern Repository 's Wiki" 기사에는이 문제에 대한 통찰력있는 주장이 있습니다. 물론이 게시물도 고려해야합니다.

예를 들어, 한 곳에서 리소스를 해제하기 위해 예를 들어 비 사용 언어로 된 단일 종료점을 원한다면 goto를 신중하게 적용하는 것이 좋습니다. 예를 들어이 다소 고안된 예를 참조하십시오 (화면 부동산을 저장하기 위해 압축).

int f(int y) {
    int value = -1;
    void *data = NULL;

    if (y < 0)
        goto clean;

    if ((data = malloc(123)) == NULL)
        goto clean;

    /* More code */

    value = 1;
clean:
   free(data);
   return value;
}

개인적으로 나는 일반적으로 여러 종료점을 싫어하는 것보다 화살표 프로그래밍을 싫어하지만 둘 다 올바르게 적용하면 유용합니다. 물론 최선의 방법은 프로그램이 어느 것도 요구하지 않도록 구성하는 것입니다. 함수를 여러 청크로 나누면 일반적으로 도움이됩니다. :)

그렇게 할 때이 예제에서와 같이 어쨌든 여러 개의 종료 점이 발생합니다. 여기 더 큰 함수는 몇 개의 작은 함수로 나뉩니다.

int g(int y) {
  value = 0;

  if ((value = g0(y, value)) == -1)
    return -1;

  if ((value = g1(y, value)) == -1)
    return -1;

  return g2(y, value);
}

프로젝트 또는 코딩 지침에 따라 대부분의 보일러 플레이트 코드는 매크로로 대체 될 수 있습니다. 참고로, 이런 식으로 분류하면 g0, g1, g2 기능을 개별적으로 테스트하기가 매우 쉽습니다.

분명히, OO 및 예외 가능 언어에서 나는 그런 if 문을 사용하지 않을 것입니다 (또는 전혀 노력을 기울이지 않고도 해결할 수 있다면) 코드는 훨씬 더 명확합니다. 비 화살. 그리고 비 최종 수익은 대부분 예외 일 것입니다.

한마디로;

  • 많은 수익보다 나은 수익은 거의 없습니다
  • 큰 화살표보다 하나 이상의 수익이 낫고 가드 조항 은 일반적으로 괜찮습니다.
  • 예외는 가능하면 대부분의 'guard 조항'을 대체 할 수 있습니다.

NULL 포인터를 해제하려고하기 때문에 y <0에 대해 예제가 충돌합니다. ;-)
Erich Kitzmueller

2
opengroup.org/onlinepubs/009695399/functions/free.html "ptr이 널 (null) 포인터이면 조치가 발생하지 않습니다."
Henrik Gustafsson

1
NULL을 free로 전달하는 것은 정의 된 no-op이므로 충돌하지 않습니다. NULL을 먼저 테스트해야한다는 성가신 오해입니다.
hlovdal

"화살표"패턴은 피할 수없는 대안이 아닙니다. 이것은 이분법입니다.
Adrian McCarthy

9

당신은 adage- beauty가 보는 사람의 눈에 있다는 것을 알고 있습니다.

일부 사람들은 NetBeans 와 일부는 IntelliJ IDEA , 일부는 Python , PHP는 맹세합니다 .

일부 상점에서는 다음과 같이 고집하면 직업을 잃을 수 있습니다.

public void hello()
{
   if (....)
   {
      ....
   }
}

문제는 가시성과 유지 보수성에 관한 것입니다.

부울 대수를 사용하여 논리와 상태 머신의 사용을 줄이고 단순화하는 데 중독되어 있습니다. 그러나 코딩에서 "수학적 기술"을 사용하는 것이 적합하지 않다고 생각한 과거 동료들이있었습니다. 그리고 그것은 나쁜 습관이 될 것입니다. 죄송합니다. 제가 사용하는 기술은 매우 눈에 잘 띄고 유지 관리가 가능합니다. 6 개월 후 코드로 돌아 오면 코드가 오히려 스파게티의 혼란을 분명하게 이해하게 될 것이기 때문입니다.

이봐 친구 (이전의 고객과 마찬가지로)는 내가 그것을 고칠 필요가있을 때 고치는 방법을 아는 한 원하는 것을합니다.

20 년 전, 내 동료가 오늘날 민첩한 개발 전략 이라고 불리는 것을 사용하여 해고 된 것을 기억 합니다. 그는 세심한 점진적 계획을 세웠다. 그러나 그의 매니저 당신은 점진적으로 사용자에게 기능을 해제 할 수 있습니다 "그에게 고함했다! 당신은 고수해야한다 폭포 ." 관리자에 대한 그의 반응은 증분 개발이 고객의 요구에 더 정확할 것이라는 것이 었습니다. 그는 고객의 요구를 충족시키기 위해 노력했지만 관리자는 "고객의 요구 사항"에 따른 코딩을 믿었습니다.

데이터 정규화, MVPMVC 경계 를 위반 한 경우가 종종 있습니다. 함수를 구성하는 대신 인라인합니다. 우리는 지름길을 취합니다.

개인적으로, 나는 PHP가 나쁜 습관이라고 생각하지만 무엇을 알고 있습니까? 모든 이론적 주장은 하나의 규칙 집합을 이행하려고 노력합니다.

품질 = 정밀성, 유지 보수성 및 수익성.

다른 모든 규칙은 배경으로 사라집니다. 물론이 규칙은 결코 사라지지 않습니다.

게으름은 훌륭한 프로그래머의 미덕입니다.


1
"이전의 고객처럼 말했듯이 친구는 내가 고칠 필요가있을 때 고치는 방법을 아는 한 원하는대로 행동한다." 문제 : '고정'하는 사람이 종종 아닙니다.
Dan Barron

나는 당신이 어디로 가고 있는지에 동의하기 때문에이 답변에 +1하지만 어떻게 거기에 도착하는지는 아닙니다. 나는 이해의 수준이 있다고 주장합니다. 즉, 직원 A가 5 년 동안 프로그래밍 경험을 쌓았고 회사에서 5 년을 지낸 것에 대한 이해는 회사를 시작한 새로운 대학 졸업생 인 직원 B와는 매우 다릅니다. 직원 A가 코드를 이해할 수있는 유일한 사람이라면 유지 관리 할 수 ​​없으므로 직원 B가 이해할 수있는 코드를 작성해야합니다. 이것이 진정한 예술이 소프트웨어에있는 곳입니다.
프랜시스 로저스

9

가드 절을 사용하여 일찍 반환하고 그렇지 않으면 메서드가 끝날 때 종료됩니다. 단일 입력 및 종료 규칙은 역사적으로 중요한 의미를 지니고 있으며 여러 개의 리턴 (및 많은 결함)이있는 단일 C ++ 메소드에 대해 10 A4 페이지로 실행되는 레거시 코드를 처리 할 때 특히 유용했습니다. 보다 최근에, 허용되는 모범 사례는 방법을 작게 유지하여 다중 출구가 이해에 대한 임피던스를 덜 만드는 것입니다. 위에서 복사 한 다음 Kronoz 예제에서 질문은 // Rest of code ... ? 에서 발생하는 것입니다 .

void string fooBar(string s, int? i) {

  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

나는 예제가 다소 고안 되었다는 것을 알고 있지만 foreach 루프를 LINQ 문으로 리팩터링하여 유혹 조항으로 간주 할 수 있습니다. 다시 한 번, 고안된 예에서 코드의 의도는 명확하지 않으며 someFunction () 은 다른 부작용이 있거나 결과가 // Rest of code ... 에서 사용될 수 있습니다 .

if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;

다음과 같은 리팩토링 된 기능 제공 :

void string fooBar(string s, int? i) {

  if (string.IsNullOrEmpty(s) || i == null) return null;
  if (someFunction(s, i).Any(r => !r.Passed)) return null;

  // Rest of code...

  return ret;
}

C ++에는 예외가 없습니까? 그렇다면 왜 null인수가 받아 들여지지 않았다는 것을 나타내는 예외를 던지는 대신에 돌아가 겠습니까?
Maarten Bodewes

1
내가 지적했듯이 예제 코드는 이전 답변 ( stackoverflow.com/a/36729/132599 ) 에서 복사됩니다 . 원래 예제는 null을 반환했으며 인수 예외를 발생시키기 위해 리팩토링은 내가 만들려고했던 시점이나 원래 질문에 중요하지 않았습니다. 좋은 습관으로, 그렇습니다. 일반적으로 (C #에서는) null 값을 반환하는 대신 가드 절에 ArgumentNullException을 발생시킵니다.
David Clarke

7

내가 생각할 수있는 한 가지 좋은 이유는 코드 유지 관리 때문입니다. 단일 종료 지점이 있습니다. 결과의 형식을 변경하려면 ..., 구현하는 것이 훨씬 간단합니다. 또한 디버깅을 위해 중단 점을 붙일 수 있습니다. :)

그러나 한 번 코딩 표준이 '함수 당 하나의 리턴 명령문'을 부과하는 라이브러리에서 작업해야했으며 꽤 힘들다는 것을 알았습니다. 나는 많은 숫자 계산 코드를 작성하고 종종 '특별한 경우'가 있으므로 코드를 따르기가 매우 어려워졌습니다 ...


그것은 실제로 차이를 만들지 않습니다. 리턴되는 지역 변수의 유형을 변경하면 모든 할당을 해당 지역 변수에 수정해야합니다. 그리고 모든 메소드 호출도 수정해야하기 때문에 어쨌든 다른 서명으로 메소드를 정의하는 것이 좋습니다.
마틴 보 데우스

@ MaartenBodewes-owlstead-차이를 만들 수 있습니다. 로컬 변수에 대한 모든 할당을 수정하거나 메소드 호출을 변경할 필요가 없는 두 개의 예제를 지정하기 위해 함수는 날짜를 문자열로 리턴 할 수 있습니다 (로컬 변수는 실제 날짜이며 문자열 형식은 마지막 순간) 또는 소수점 이하 자릿수를 반환하고 소수점 이하 자릿수를 변경하려고 할 수 있습니다.
nnnnnn

@nnnnnn OK, 출력에서 ​​포스트 프로세싱을 원한다면 포스트 프로세싱을 수행하는 새로운 메소드를 생성하고 이전 메소드는 그대로 둡니다. 그건 단지의 약간 어렵게 리팩토링, 그러나 다른 통화 어쨌든 새로운 형식과 호환되는 경우 확인해야합니다. 그러나 이것이 확실한 이유입니다.
마틴 보 데우스

7

여러 개의 종료점은 작은 기능, 즉 한 화면 길이에서 전체적으로 볼 수있는 기능에 적합합니다. 긴 함수에 여러 개의 종료 점이 포함되어 있으면 함수를 더 잘게 다질 수 있다는 표시입니다.

그것은 절대적으로 필요한 경우가 아니라면 다중 종료 기능을 피한다고 말했습니다 . 좀 더 복잡한 기능으로 가려운 줄에서 약간의 반환으로 인한 버그의 고통을 느꼈습니다.


6

나는 당신에게 단일 출구 경로를 강요하는 끔찍한 코딩 표준으로 작업했으며 그 기능이 사소한 것이 아니라면 결과는 거의 항상 구조화되지 않은 스파게티입니다. 많은 휴식으로 끝나고 계속 방해합니다.


스킵하는 당신의 마음을 이야기하는 것을 말할 것도없고 if각 메서드 호출이 반환 성공 여부 :( 앞에 문을
마틴 Bodewes

6

단일 종료점 (다른 모든 것)은 코드를 훨씬 더 읽기 쉽게 만듭니다. 그러나 인기가 있습니다.

resulttype res;
if if if...
return res;

"res ="가 "return"보다 낫지는 않습니다. 단일 return 문이 있지만 실제로 함수가 끝나는 여러 지점이 있습니다.

여러 개의 리턴 (또는 "res ="s) 함수가있는 경우 단일 종료점을 사용하여 여러 개의 작은 함수로 나누는 것이 좋습니다.


6

나의 일반적인 정책은 코드의 복잡성을 더 추가하여 코드의 복잡성을 크게 줄이지 않으면 함수 끝에 하나의 return 문 만 갖는 것입니다. 사실, 나는 에펠 탑의 팬인데 반환 진술을하지 않아도 유일한 반환 규칙을 시행합니다 (결과를 자동으로 생성하는 '결과'변수가 있습니다).

명백한 버전이없는 것보다 여러 번 리턴하여 코드를 명확하게 할 수있는 경우가 있습니다. 여러 개의 return 문없이 이해할 수없는 너무 복잡한 함수가있는 경우 더 많은 재 작업이 필요하다고 주장 할 수 있지만 때로는 그러한 것들에 대해 실용적이기는 좋습니다.


5

몇 번의 반환으로 끝나면 코드에 문제가있을 수 있습니다. 그렇지 않으면 때로는 코드를 더 깨끗하게 만들 때 서브 루틴의 여러 위치에서 돌아올 수 있다는 것이 때때로 동의 할 것입니다.

펄 6 : 나쁜 예

sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}

이처럼 더 잘 작성 될 것입니다

펄 6 : 좋은 예

@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}

이것은 간단한 예일뿐입니다.


Ok 왜 투표가 거부 되었습니까? 의견이 아닌 것 같지 않습니다.
브래드 길버트

5

마지막에 단일 반환에 대한 지침으로 투표합니다. 이것은 일반적인 코드 정리 처리에 도움이됩니다 ... 예를 들어 다음 코드를 살펴보십시오 ...

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}

C 코드에서 단일 반환에 투표합니다. 그러나 가비지 수집 및 최종 차단을 시도한 언어로 코딩하는 경우 어떻게해야합니까?
Anthony

4

이것은 아마도 특이한 관점이지만, 여러 개의 return 문을 선호한다고 믿는 사람은 4 개의 하드웨어 중단 점 만 지원하는 마이크로 프로세서에서 디버거를 사용할 필요가 없다고 생각합니다. ;-)

"화살표 코드"문제는 완전히 정확하지만 여러 return 문을 사용할 때 사라지는 문제는 디버거를 사용하는 상황에 있습니다. 출구를보고 복귀 조건을 볼 수 있도록 중단 점을 놓을 편리한 포괄 위치는 없습니다.


5
그것은 다른 종류의 조기 최적화입니다. 특별한 경우를 위해 최적화해서는 안됩니다. 코드의 특정 섹션을 많이 디버깅하면 종료점 수보다 더 잘못됩니다.
웨지

디버거에 따라 다릅니다.
Maarten Bodewes

4

함수에 리턴 문이 많을수록 해당 메소드의 복잡도가 높아집니다. 리턴 문이 너무 많은지 궁금해하는 경우 해당 함수에 너무 많은 코드 행이 있는지 스스로에게 물어볼 수 있습니다.

그러나 하나 / 다수의 반환 진술에는 아무런 문제가 없습니다. 일부 언어에서는 다른 언어 (C)보다 더 나은 방법 (C ++)입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.