함수에 종료 지점이 하나만 있어야하는 이유는 무엇입니까? [닫은]


97

가독성과 효율성을 잃기 때문에 단일 종료점 함수가 코딩에 나쁜 방법이라고 항상 들었습니다. 나는 아무도 반대편을 주장하는 것을 들어 본 적이 없습니다.

나는 이것이 CS와 관련이 있다고 생각했지만이 질문은 cstheory stackexchange에서 떨어졌습니다.



6
대답은 항상 옳은 대답이 없다는 것입니다. 나는 종종 여러 개의 이탈로 코딩하는 것이 더 쉽다고 생각합니다. 또한 (위의 코드를 업데이트 할 때) 동일한 다중 이탈로 인해 코드를 수정 / 확장하는 것이 더 어렵다는 것을 발견했습니다. 사례별로 이러한 결정을 내리는 것이 우리의 임무입니다. 결정에 항상 "최상의"답변이있을 때 우리는 필요하지 않습니다.
JS.

1
파시스트 개조 마지막 두 가지 질문을 제거한 @finnw, 다시 그들이 다시 다시 대답하여되어야 할 것이다 있는지 확인하고합니다
마틴 Bodewes

질문에서 "논쟁"이라는 단어에도 불구하고 저는 이것이 의견 기반 질문이라고 생각하지 않습니다. 좋은 디자인 등과 관련이 있습니다. 폐쇄 할 이유는 없지만 w / e.
Ungeheuer 2011

1
단일 종료 지점은 디버깅, 읽기, 성능 측정 및 튜닝, 리팩토링을 단순화합니다. 이는 객관적이고 실질적으로 중요합니다. (간단한 인수 확인 후) 조기 반환을 사용하면 두 스타일을 합리적으로 혼합 할 수 있습니다. 단일 출구 점의 이점을 감안할 때 반환 값으로 코드를 흩 뜨리는 것은 단순히 게으르고 엉성하고 부주의 한 프로그래머의 증거이며 적어도 강아지를 좋아하지 않을 수도 있습니다.
Rick O'Shea

답변:


108

생각에는 여러 학파가 있으며 주로 개인적 선호도에 달려 있습니다.

하나는 출구 지점이 하나뿐이면 덜 혼란 스럽습니다. 메소드를 통해 단일 경로가 있고 출구를 찾을 위치를 알고 있습니다. 마이너스 쪽에서 들여 쓰기를 사용하여 중첩을 표시하면 코드가 오른쪽으로 크게 들여 쓰기되어 모든 중첩 범위를 따르는 것이 매우 어려워집니다.

또 다른 방법은 전제 조건을 확인하고 메서드 시작시 일찍 종료 할 수 있으므로 메서드 본문 전체가 오른쪽으로 5 마일 떨어져 들여 쓰기되지 않고 특정 조건이 참임을 메서드 본문에서 알 수 있다는 것입니다. 이는 일반적으로 걱정해야하는 범위의 수를 최소화하므로 코드를 훨씬 쉽게 따라갈 수 있습니다.

세 번째는 원하는 곳 어디에서나 나갈 수 있다는 것입니다. 예전에는 더 혼란 스러웠지만 이제는 도달 할 수없는 코드를 감지하는 구문 색상 편집기와 컴파일러가 있으므로 처리하기가 훨씬 쉽습니다.

나는 중간 캠프에 있습니다. 단일 종료 지점을 적용하는 것은 무의미하거나 심지어 비생산적인 제한입니다. 실행. 그러나 방법을 "게이팅"하면 방법 본문을 크게 단순화 할 수 있습니다.


1
singe exit패러다임 에서 딥 네 스팅은 go to문장의 섬세함 으로 제거 될 수 있습니다 . 또한 함수의 로컬 Error레이블 아래에서 일부 후 처리를 수행 할 수있는 기회를 얻습니다. 이는 여러 returns 로는 불가능 합니다.
Ant_222 2015 년

2
일반적으로 갈 필요를 피하는 좋은 솔루션이 있습니다. 나는 'return (Fail (...))'을 선호하고 공유 정리 코드를 Fail 메서드에 넣는 것을 선호합니다. 이것은 메모리를 해제하기 위해 몇 개의 로컬을 전달해야 할 수도 있지만, 성능이 중요한 코드 비트가 아니라면 일반적으로 goto IMO보다 훨씬 깨끗한 솔루션입니다. 또한 여러 메서드가 유사한 정리 코드를 공유 할 수도 있습니다.
Jason Williams

객관적인 기준에 기반한 최적의 접근 방식이 있지만 우리는 생각의 학파 (정확하고 부정확)가 있고 개인 선호도 (정확한 접근 방식에 대한 선호 또는 반대)에 따라 결정된다는 데 동의 할 수 있습니다.
Rick O'Shea

39

일반적인 권장 사항은 return 문이 가능한 경우 부작용이있는 첫 번째 코드 앞이나 부작용이있는 마지막 코드 뒤에 위치해야한다는 것입니다. 나는 다음과 같은 것을 고려할 것입니다.

  if (! argument) // null이 아닌지 확인
    return ERR_NULL_ARGUMENT;
  ... 널이 아닌 인수 처리
  만약 (OK)
    반환 0;
  그밖에
    반환 ERR_NOT_OK;

보다 명확 :

  int return_value;
  if (argument) // 널이 아님
  {
    .. 널이 아닌 인수 처리
    .. 결과를 적절하게 설정
  }
  그밖에
    결과 = ERR_NULL_ARGUMENT;
  반환 결과;

특정 조건으로 인해 함수가 어떤 작업도 수행하지 못하도록해야하는 경우 함수가 어떤 작업을 수행 할 수있는 지점보다 높은 지점에서 함수에서 일찍 복귀하는 것을 선호합니다. 함수가 부작용이있는 작업을 수행 한 후에는 모든 부작용을 처리해야 함을 명확히하기 위해 바닥에서 돌아 오는 것을 선호합니다.


ok변수를 관리하는 첫 번째 예 는 나에게 단일 반환 방식으로 보입니다. 또한 if-else 블록은 다음과 같이 간단히 다시 작성할 수 있습니다.return ok ? 0 : ERR_NOT_OK;
Melebius

2
첫 번째 예제는 return모든 것을 수행하는 모든 코드 앞에 시작 부분이 있습니다. ?:연산자 사용과 관련 하여 별도의 줄에 작성하면 많은 IDE에서 디버그 중단 점을 비정상 시나리오에 쉽게 연결할 수 있습니다. BTW, "단일 종료점" 의 진짜 열쇠는 중요한 것은 정상 함수에 대한 각각의 특정 호출에 대해 종료 점이 호출 직후의 지점 이라는 것을 이해하는 데 있습니다 . 요즘 프로그래머들은 그것을 당연한 것으로 여기지만 항상 그런 것은 아닙니다. 드물게 코드가 스택 공간없이
지나 가야

... 조건부 또는 계산 된 gotos를 통해 종료됩니다. 일반적으로 어셈블리 언어 이외의 다른 언어로 프로그래밍 할 수있는 충분한 리소스가있는 모든 항목은 스택을 지원할 수 있지만 매우 엄격한 제약 조건 (한 경우에는 ZERO 바이트의 RAM까지)에서 작동해야하는 어셈블리 코드를 작성했습니다. 이러한 경우 여러 출구 지점이 있으면 도움이 될 수 있습니다.
supercat 2011

1
소위 더 명확한 예는 훨씬 덜 명확하고 읽기 어렵습니다. 하나의 종료 지점은 항상 읽기 쉽고 유지 관리가 쉽고 디버그하기 쉽습니다.
GuidoG 2011

8
@GuidoG : 생략 된 섹션에 표시되는 내용에 따라 두 패턴 모두 더 읽기 쉬울 수 있습니다. "return x;"사용 명령문에 도달하면 반환 값이 x가 될 것임을 분명히합니다. "result = x;"사용 결과가 반환되기 전에 다른 것이 결과를 변경할 가능성을 열어 둡니다. 실제로 결과를 변경해야하는 경우 유용 할 수 있지만 코드를 검사하는 프로그래머는 대답이 "불가능"인 경우에도 결과가 어떻게 변경되는지 확인하기 위해 코드를 검사해야합니다.
supercat 16.11.

15

단일 진입 점과 출구 점은 구조화 된 프로그래밍과 단계별 스파게티 코딩의 원래 개념이었습니다. 변수에 할당 된 메모리 공간을 적절하게 정리해야하므로 여러 종료점 함수에 더 많은 코드가 필요하다는 믿음이 있습니다. 함수가 변수 (리소스)를 할당하고 적절한 정리없이 조기에 함수에서 나가면 리소스 누수가 발생하는 시나리오를 고려하십시오. 또한 모든 종료 전에 정리를 구성하면 많은 중복 코드가 생성됩니다.


그것은 RAII의 문제가 아닙니다
BigSandwich

14

대부분의 경우 결과물의 요구에 따라 결정됩니다. "예전에는"여러 반환 지점이있는 스파게티 코드가 메모리 누수를 유발했습니다. 그 방법을 선호하는 코더는 일반적으로 잘 정리되지 않았기 때문입니다. 또한 일부 컴파일러가 중첩 된 범위에서 반환하는 경우 반환 중에 스택이 팝될 때 반환 변수에 대한 참조를 "손실"하는 문제가있었습니다. 보다 일반적인 문제는 함수의 호출 상태가 반환 상태와 정확히 동일하도록 시도하는 재진입 코드입니다. oop의 돌연변이가 이것을 위반했고 개념은 보류되었습니다.

여러 출구 지점이 제공하는 속도가 필요한 결과물, 특히 커널이 있습니다. 이러한 환경에는 일반적으로 자체 메모리 및 프로세스 관리 기능이 있으므로 누수 위험이 최소화됩니다.

개인적으로 단일 종료 지점을 갖는 것을 좋아합니다. 종종이를 사용하여 return 문에 중단 점을 삽입하고 코드가 해당 솔루션을 결정하는 방법에 대한 코드 검사를 수행하기 때문입니다. 나는 단지 입구로 가서 단계를 통과 할 수 있으며, 광범위하게 중첩되고 재귀적인 솔루션을 사용합니다. 코드 검토 자로서 함수의 다중 반환에는 훨씬 더 깊은 분석이 필요합니다. 따라서 구현 속도를 높이기 위해 수행하는 경우 Peter를 강탈하여 Paul을 구하는 것입니다. 코드 검토에 더 많은 시간이 필요하므로 효율적인 구현에 대한 가정이 무효화됩니다.

-2 센트

자세한 내용은이 문서를 참조하십시오 : NISTIR 5459


8
multiple returns in a function requires a much deeper analysis함수가 이미 큰 경우에만 (> 1 화면), 그렇지 않으면 분석이 더 쉬워집니다
dss539

2
다중 수익은 분석을 더 쉽게 만들지 않습니다. 그 반대
일뿐입니다

1
링크가 끊어졌습니다 (404).
fusi

1
@fusi - archive.org에 그것을 발견하고 여기에 링크를 업데이트
sscheider

4

제 생각에는 한 지점에서만 함수 (또는 다른 제어 구조)를 종료하라는 조언이 종종 과매도되었습니다. 일반적으로 한 지점에서만 나가는 이유는 두 가지입니다.

  1. 단일 종료 코드는 읽고 디버그하기가 더 쉽습니다. (나는이 이유에 대해 많이 생각하지 않지만 주어진 것은 인정한다. 읽고 디버그하기 훨씬 쉬운 것은 단일 입력 코드이다.)
  2. 단일 종료 코드가 링크되고 더 깔끔하게 반환됩니다.

두 번째 이유는 미묘하고 특히 함수가 큰 데이터 구조를 반환하는 경우 장점이 있습니다. 그러나 나는 그것에 대해 너무 걱정하지 않을 것입니다.

학생이라면 수업에서 최고 점수를 받고 싶습니다. 강사가 선호하는 것을하십시오. 그는 아마도 그의 관점에서 타당한 이유가있을 것입니다. 그래서 최소한 그의 관점을 배울 것입니다. 이것은 그 자체로 가치가 있습니다.

행운을 빕니다.


4

나는 단일 출구 스타일의 옹호자였습니다. 내 추론은 대부분 고통에서 비롯되었습니다 ...

단일 종료는 디버그하기가 더 쉽습니다.

오늘날 우리가 가지고있는 기술과 도구를 감안할 때 단위 테스트 및 로깅으로 인해 단일 종료가 불필요하게 될 수 있으므로 이것은 훨씬 덜 합리적인 입장입니다. 즉, 디버거에서 코드가 실행되는 것을 관찰해야 할 때 여러 종료 지점이 포함 된 코드를 이해하고 작업하는 것이 훨씬 더 어려웠습니다.

이것은 상태를 검사하기 위해 할당을 삽입해야 할 때 특히 그렇습니다 (최신 디버거에서 watch 표현식으로 대체 됨). 문제를 숨기거나 실행을 완전히 중단시키는 방식으로 제어 흐름을 변경하는 것도 너무 쉬웠습니다.

단일 종료 방법은 디버거에서 단계를 진행하기 쉬웠고 논리를 깨지 않고 분리하기가 더 쉬웠습니다.


0

대답은 상황에 따라 매우 다릅니다. GUI를 만들고 API를 초기화하고 메인 시작시 창을 여는 함수가있는 경우 오류를 발생시킬 수있는 호출로 가득 차게되며, 각 호출로 인해 프로그램 인스턴스가 닫힙니다. 중첩 된 IF 문을 사용하고 들여 쓰기하면 코드가 오른쪽으로 매우 치우칠 수 있습니다. 각 단계에서 오류를 반환하는 것이 코드에 몇 개의 플래그를 사용하여 디버그하는 것만큼이나 쉬우면서 더 좋고 실제로 더 읽기 쉬울 수 있습니다.

그러나 다른 조건을 테스트하고 메서드의 결과에 따라 다른 값을 반환하는 경우 단일 종료 지점을 사용하는 것이 훨씬 더 좋습니다. 저는 MATLAB에서 매우 커질 수있는 이미지 처리 스크립트 작업을했습니다. 여러 개의 종료 점이 코드를 따라 가기 어렵게 만들 수 있습니다. Switch 문이 훨씬 더 적절했습니다.

가장 좋은 방법은 당신이가는 동안 배우는 것입니다. 무언가에 대한 코드를 작성하는 경우 다른 사람의 코드를 찾아서 구현 방법을 확인하십시오. 좋아하는 비트와 싫어하는 비트를 결정하십시오.


-6

함수에 여러 개의 종료 점이 필요하다고 생각되면 함수가 너무 커서 너무 많은 작업을 수행하는 것입니다.

Robert C. Martin의 저서 Clean Code에서 함수에 대한 장을 읽는 것이 좋습니다.

기본적으로 4 줄 이하의 코드로 함수를 작성해야합니다.

Mike Long의 블로그 에서 몇 가지 메모 :

  • 기능의 첫 번째 규칙 : 작아야합니다
  • 두 번째 기능 규칙 : 그보다 작아야합니다.
  • if 문, while 문, for 루프 등의 블록은 한 줄 길이 여야합니다.
  • … 그리고 그 코드 줄은 일반적으로 함수 호출이됩니다.
  • 들여 쓰기 수준은 한 개 또는 두 개를 넘지 않아야합니다.
  • 함수는 한 가지를 수행해야합니다
  • 함수 문은 모두 동일한 수준의 추상화 여야합니다.
  • 함수에는 3 개 이하의 인수가 있어야합니다.
  • 출력 인수는 코드 냄새입니다.
  • 부울 플래그를 함수에 전달하는 것은 정말 끔찍합니다. 정의에 따라 함수에서 두 가지 작업을 수행합니다.
  • 부작용은 거짓말입니다.

29
4 줄? 이러한 단순성을 가능하게하는 코드는 무엇입니까? 예를 들어 Linux 커널이나 git이 그렇게하는지 정말로 의심합니다.
신조

7
"부울 플래그를 함수에 전달하는 것은 정말 끔찍합니다. 정의에 따라 함수에서 두 가지 작업을 수행하는 것입니다." 정의에 따라? 아니요 ... 그 부울은 잠재적으로 네 줄 중 하나에 만 영향을 미칠 수 있습니다. 또한 기능 크기를 작게 유지하는 데 동의하지만 4는 너무 제한적입니다. 이것은 매우 느슨한 지침으로 받아 들여 져야합니다.
제시

12
이와 같은 제한을 추가하면 필연적으로 코드를 혼란스럽게 만들 수 있습니다. 방법이 간결하고 불필요한 부작용없이 의도 된 작업 만 수행하는 방법에 대한 것입니다.
Jesse

10
내가 여러 번 반대 투표를 할 수 있기를 바라는 드문 답변 중 하나입니다.
Steven Rands 2017

8
안타깝게도이 답변은 여러 가지 작업을 수행하고 있으며 아마도 4 줄 미만의 여러 다른 작업으로 나눌 필요가있을 것입니다.
엘리
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.