if 문-단락 평가 대 가독성


90

때로는 if문이 다소 복잡하거나 길 수 있으므로 가독성을 위해 복잡한 호출을 if.

예 :

if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
    // do stuff
}

이것으로

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();

if (b1 || b2)
{
    //do stuff
}

(제공되는 예는없는 것을 여러 인수 등 다른 통화를 상상 ... 그냥 그림을 위해이다, 나쁜)

그러나이 추출로 단락 평가 (SCE)를 잃었습니다.

  1. 매번 SCE를 잃게 되나요? 컴파일러가 "최적화"하고 여전히 SCE를 제공 할 수있는 시나리오가 있습니까?
  2. SCE를 잃지 않고 두 번째 스 니펫의 향상된 가독성을 유지할 수있는 방법이 있습니까?

20
연습은 여기 또는 다른 장소에서 볼 수있는 성능에 대한 대부분의 답변이 대부분의 경우 잘못되었음을 보여줍니다 (4 개 잘못 1 개 정답). 제 조언은 항상 프로파일 링을하고 직접 확인하는 것입니다. "조기 최적화"를 피하고 새로운 것을 배우게 될 것입니다.
Marek R

25
@MarekR는 ... 그것은 OtherCunctionCall의 부작용에 관한 것입니다,뿐만 아니라 성능에 대해입니다
relaxxx

3
다른 사이트를 참조 할 때 것을 @ 데이비드,이 점에 종종 도움이 교차 게시물이 눈살을 찌푸리게한다
모기

7
가독성이 주요 관심사라면 if 조건부 내부에 부작용이있는 함수를 호출하지 마십시오
Morgen

3
잠재적 유권자 : 질문을 다시 읽으십시오. 파트 (1)은 의견 기반 이 아닌 반면, 파트 (2)는 내가하려는 "모범 사례"에 대한 참조를 제거하는 편집을 통해 의견 기반이되는 것을 쉽게 멈출 수 있습니다.
duplode

답변:


119

하나의 자연스러운 솔루션은 다음과 같습니다.

bool b1 = SomeCondition();
bool b2 = b1 || SomeOtherCondition();
bool b3 = b2 || SomeThirdCondition();
// any other condition
bool bn = bn_1 || SomeFinalCondition();

if (bn)
{
  // do stuff
}

이것은 이해하기 쉽고 모든 경우에 적용 할 수 있으며 단락 동작이 있다는 이점이 있습니다.


이것이 나의 초기 솔루션이었습니다. 메서드 호출과 for-loop 본문의 좋은 패턴은 다음과 같습니다.

if (!SomeComplicatedFunctionCall())
   return; // or continue

if (!SomeOtherComplicatedFunctionCall())
   return; // or continue

// do stuff

하나는 단락 평가와 동일한 성능 이점을 얻지 만 코드가 더 읽기 쉬워 보입니다.


4
@relaxxx : 알겠습니다.하지만 "이후에 할 일이 더 많다 if"는 것은 함수 나 메서드가 너무 커서 더 작은 것으로 분할해야한다는 신호이기도합니다. 항상 최선의 방법은 아니지만 자주 그렇습니다!
nperson325681

2
이 화이트리스트 원칙을 위반
JoulinRouge

13
@JoulinRouge : 흥미 롭습니다. 저는이 원리에 대해 들어 본 적이 없습니다. 필자는 가독성에 대한 이점을 위해이 "단락"접근 방식을 선호합니다. 이는 들여 쓰기를 줄이고 들여 쓰기 된 블록 뒤에 무언가가 발생할 가능성을 제거합니다.
Matthieu M.

2
더 읽기 쉬운가요? b2적절하게 이름을 지정 하면 someConditionAndSomeotherConditionIsTrue의미있는 것이 아니라를 얻을 수 있습니다. 또한이 연습 중에 정신 스택에 많은 변수를 유지해야합니다 (이 범위에서 작업을 중단 할 때까지 tbh). 나는 SJuan76의 2 번 솔루션을 사용하거나 전체를 함수에 넣을 것입니다.
Nathan Cooper

2
모든 주석을 읽지는 못했지만 빠른 검색 후 디버깅이라는 첫 번째 코드 조각의 큰 이점을 찾지 못했습니다. 미리 변수에 할당하는 대신 if 문에 직접 배치 한 다음 대신 변수를 사용하면 필요한 것보다 디버깅이 더 어려워집니다. 변수를 사용하면 값을 의미 적으로 함께 그룹화하여 가독성을 높일 수 있습니다.
rbaleksandar

31

조건을 여러 줄로 나누는 경향이 있습니다.

if( SomeComplicatedFunctionCall()
 || OtherComplicatedFunctionCall()
  ) {

여러 연산자 (&&)를 다룰 때에도 각 대괄호 쌍으로 들여 쓰기 만하면됩니다. SCE는 여전히 시작됩니다-변수를 사용할 필요가 없습니다. 이런 식으로 코드를 작성하면 이미 몇 년 동안 더 쉽게 읽을 수있게되었습니다. 더 복잡한 예 :

if( one()
 ||( two()> 1337
  &&( three()== 'foo'
   || four()
    )
   )
 || five()!= 3.1415
  ) {

28

긴 조건 체인이 있고 단락을 유지해야 할 사항이있는 경우 임시 변수를 사용하여 여러 조건을 결합 할 수 있습니다. 예를 들어 다음과 같이 할 수 있습니다.

bool b = SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
if (b && some_other_expression) { ... }

C ++ 11 지원 컴파일러 가있는 경우 위와 유사한 람다 식 을 사용하여 식을 함수로 결합 할 수 있습니다 .

auto e = []()
{
    return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
};

if (e() && some_other_expression) { ... }

21

1) 예, 더 이상 SCE가 없습니다. 그렇지 않으면

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();

if나중에 진술 이 있는지 여부에 따라 어떤 방식 으로든 작동합니다 . 너무 복잡합니다.

2) 이것은 의견 기반이지만 합리적으로 복잡한 표현의 경우 다음을 수행 할 수 있습니다.

if (SomeComplicatedFunctionCall()
    || OtherComplicatedFunctionCall()) {

너무 복잡한 경우, 분명한 해결책은 표현식을 평가하고 호출하는 함수를 만드는 것입니다.


21

다음을 사용할 수도 있습니다.

bool b = someComplicatedStuff();
b = b || otherComplicatedStuff(); // it has to be: b = b || ...;  b |= ...; is bitwise OR and SCE is not working then 

SCE가 작동합니다.

그러나 예를 들면 다음과 같이 훨씬 읽기 쉽습니다.

if (
    someComplicatedStuff()
    ||
    otherComplicatedStuff()
   )

3
부울과 비트 연산자를 결합하는 것을 좋아하지 않습니다. 일반적으로 나는 매우 낮은 수준으로 작업하고 프로세서 사이클 수를 계산하지 않는 한 가장 읽기 쉬운 것을 사용합니다.
개미

3
나는 특별히 사용했고 b = b || otherComplicatedStuff();@SargeBorsch는 SCE를 제거하기 위해 편집합니다. @Ant 변경 사항에 대해 알려 주셔서 감사합니다.
KIIV

14

1) 매번 SCE를 잃게됩니까? 컴파일러가 "최적화"하고 여전히 SCE를 제공 할 수있는 시나리오가 있습니까?

이러한 최적화가 허용되지 않는다고 생각합니다. 특히 OtherComplicatedFunctionCall()부작용이있을 수 있습니다.

2) 그러한 상황에서 가장 좋은 방법은 무엇입니까? (SCE를 원할 때) "가능한 한 읽을 수 있도록 포맷"하면 필요한 모든 것을 내부에 직접 넣을 수 있습니까?

설명이 포함 된 하나의 함수 또는 하나의 변수로 리팩토링하는 것을 선호합니다. 단락 평가와 가독성을 모두 유지합니다.

bool getSomeResult() {
    return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall();
}

...

if (getSomeResult())
{
    //do stuff
}

그리고 우리가 구현 등 getSomeResult()을 기반으로 SomeComplicatedFunctionCall()하고 OtherComplicatedFunctionCall()그들은 여전히 복잡하고 있다면 우리는 재귀 적 분해 수 있습니다.


2
래퍼 함수에 설명적인 이름 (아마도 getSomeResult는 아님)을 지정하여 가독성을 얻을 수 있기 때문에이 기능이 마음에 듭니다. 너무 많은 다른 답변은 가치있는 것을 추가하지 않습니다
aw04

9

1) 매번 SCE를 잃게됩니까? 컴파일러가 "최적화"하고 여전히 SCE를 제공 할 수있는 시나리오가 있습니까?

아니요,하지만 다르게 적용됩니다.

if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
    // do stuff
}

여기에서, 컴파일러는도 실행되지 않습니다 OtherComplicatedFunctionCall()경우 SomeComplicatedFunctionCall()true를 반환합니다.

bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();

if (b1 || b2)
{
    //do stuff
}

여기에서 두 함수 모두 b1및에 저장되어야하기 때문에 실행됩니다 b2. b1 == true그러면 Ff b2는 평가되지 않습니다 (SCE). 그러나 OtherComplicatedFunctionCall()이미 실행되었습니다.

경우 b2아무데도 사용되는 컴파일러 수있는 내부 함수 호출을 인라인하는 스마트 충분 기능이 관측 부작용이없는 경우 경우.

2) 그러한 상황에서 가장 좋은 방법은 무엇입니까? (SCE를 원할 때) "가능한 한 읽을 수 있도록 포맷"하면 필요한 모든 것을 내부에 직접 넣을 수 있습니까?

조건에 따라서. 부작용이나 함수의 성능 저하로 인해 실행 해야 합니까? OtherComplicatedFunctionCall()그러면 가독성을 위해 두 번째 방법을 사용해야합니다. 그렇지 않으면 첫 번째 접근 방식을 통해 SCE를 고수하십시오.


8

단락 및 조건이 한곳에있는 또 다른 가능성 :

bool (* conditions [])()= {&a, &b, ...}; // list of conditions
bool conditionsHold = true;
for(int i= 0; i < sizeOf(conditions); i ++){
     if (!conditions[i]()){;
         conditionsHold = false;
         break;
     }
}
//conditionsHold is true if all conditions were met, otherwise false

루프를 함수에 넣고 함수가 조건 목록을 받아들이고 부울 값을 출력하도록 할 수 있습니다.


1
@Erbureth 아니 그들은 아닙니다. 배열의 요소는 함수 포인터이며, 함수가 루프에서 호출 될 때까지 실행되지 않습니다.
Barmar

Barmar에게 감사합니다.하지만 편집하기 전에 Erbureth가 옳았습니다 (제 편집 내용이 시각적으로 더 직접적으로 전파 될 것이라고 생각했습니다).
levilime

4

매우 이상합니다. 아무도 코드 내에서 주석 사용을 언급하지 않을 때 가독성에 대해 이야기하고 있습니다.

if (somecomplicated_function() || // let me explain what this function does
    someother_function())         // this function does something else
...

그 위에 항상 함수 자체, 입력 및 출력에 대한 몇 가지 주석을 함수 앞에 올렸으며, 때로는 여기에서 볼 수 있듯이 예제를 넣었습니다.

/*---------------------------*/
/*! interpolates between values
* @param[in] X_axis : contains X-values
* @param[in] Y_axis : contains Y-values
* @param[in] value  : X-value, input to the interpolation process
* @return[out]      : the interpolated value
* @example          : interpolate([2,0],[3,2],2.4) -> 0.8
*/
int interpolate(std::vector<int>& X_axis, std::vector<int>& Y_axis, int value)

댓글에 사용할 형식은 개발 환경 (Visual Studio, Eclipse의 JavaDoc 등)에 따라 달라질 수 있습니다.

SCE에 관한 한, 나는 이것이 당신이 다음을 의미한다고 가정합니다.

bool b1;
b1 = somecomplicated_function(); // let me explain what this function does
bool b2 = false;
if (!b1) {                       // SCE : if first function call is already true,
                                 // no need to spend resources executing second function.
  b2 = someother_function();     // this function does something else
}

if (b1 || b2) {
...
}

-7

회사에서 일하고 다른 사람이 코드를 읽을 경우 가독성이 필요합니다. 자신을 위해 프로그램을 작성하는 경우 이해 가능한 코드를 위해 성능을 희생하고 싶은지 여부는 사용자에게 달려 있습니다.


23
"6 개월 후의 당신"은 확실히 "다른 사람"이고 "내일의 당신"은 때때로 그럴 수 있음 을 명심하십시오 . 성능 문제가 있다는 확실한 증거가있을 때까지 성능을 위해 가독성을 희생하지 않았습니다.
마틴 보너는 모니카 지원
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.