if-else 문의 전환 장점


168

switch문 을 사용하는 방법과 약 10 개의 작업이 예상되는 if30 개의 unsigned열거 형에 대한 문 을 사용하는 가장 좋은 방법은 무엇입니까 (현재는 같은 작업 임). 성능과 공간을 고려해야하지만 중요하지는 않습니다. 나는 발췌 문장을 추상화 했으므로 명명 규칙에 대해 나를 싫어하지 마십시오.

switch 성명서:

// numError is an error enumeration type, with 0 being the non-error case
// fire_special_event() is a stub method for the shared processing

switch (numError)
{  
  case ERROR_01 :  // intentional fall-through
  case ERROR_07 :  // intentional fall-through
  case ERROR_0A :  // intentional fall-through
  case ERROR_10 :  // intentional fall-through
  case ERROR_15 :  // intentional fall-through
  case ERROR_16 :  // intentional fall-through
  case ERROR_20 :
  {
     fire_special_event();
  }
  break;

  default:
  {
    // error codes that require no additional action
  }
  break;       
}

if 성명서:

if ((ERROR_01 == numError)  ||
    (ERROR_07 == numError)  ||
    (ERROR_0A == numError)  || 
    (ERROR_10 == numError)  ||
    (ERROR_15 == numError)  ||
    (ERROR_16 == numError)  ||
    (ERROR_20 == numError))
{
  fire_special_event();
}

26
'주관적'으로 편집 되었습니까? 정말? 분명히 '주관적'은 어떤 식 으로든 입증 될 수없는 것들을위한 것입니까?
Alexandra Franks

물론 가장 효율적인 코드를 생성하는 시점에서 볼 수 있지만 최신 컴파일러는 모두 효율적이어야합니다. 결국, 이것은 자전거 창고의 색상에 대한 문제입니다.
jfs

8
나는 이것이 주관적이라고 생각하지 않는다. 간단한 ASM 차이는 중요하지만 대부분의 경우 몇 초의 최적화를 무시할 수는 없습니다. 그리고이 질문에서 그것은 종교적 전쟁이나 논쟁이 아니며, 왜 더 빠를 지에 대한 합리적인 설명이 있습니다. 허용 된 답변을 읽으십시오.
chakrit


@RichardFranks 오프 주제 : grats! 당신은 내가 본 것 중 첫 번째 사람이 중재를 인수했습니다
jungle_mole

답변:


162

스위치를 사용하십시오.

최악의 경우 컴파일러는 if-else 체인과 동일한 코드를 생성하므로 아무것도 잃지 않습니다. 의심스러운 경우 가장 일반적인 경우를 switch 문에 먼저 넣으십시오.

가장 좋은 경우 옵티마이 저는 코드를 생성하는 더 좋은 방법을 찾을 수 있습니다. 컴파일러가 수행하는 일반적인 작업은 이진 결정 트리를 작성하거나 (평균 경우 비교 및 ​​점프) 점프 테이블을 작성하는 것입니다 (비교없이 작동).


2
기술적으로 열거 형 값이 점프 테이블 내에 있는지 확인하기 위해 여전히 하나의 비교가 있습니다.
0124816

p 사실입니다. 열거 형을 켜고 모든 경우를 처리하면 마지막 비교가 제거 될 수 있습니다.
Nils Pipenbrinck

4
이론적으로 일련의 ifs는 컴파일러의 스위치와 동일한 것으로 분석 될 수 있지만 왜 기회가 있습니까? 스위치를 사용하면 원하는 것을 정확하게 전달할 수 있으므로 코드 생성이 쉬워집니다.
jakobengblom2

5
jakoben : 그것은 가능하지만 스위치와 같은 if / else 체인에서만 가능합니다. 실제로 이것은 프로그래머가 스위치를 사용하기 때문에 발생하지 않습니다. 나는 컴파일러 기술에 대해 깊이 파고 들었다. "무용 한"구조를 찾는 데 많은 시간이 걸린다. 컴파일러 사용자에게는 이러한 최적화가 의미가 있습니다.
Nils Pipenbrinck

5
의사 재귀 구축의 용이성과 @NilsPipenbrinck if- else템플릿 메타 프로그래밍에 체인을, 그리고 생성의 어려움 switch case체인을, 그 매핑이 더 중요한 될 수 있습니다. (그리고 예, 고대의 의견이지만 웹은 영원히, 또는 적어도 다음 화요일까지)
Yakk-Adam Nevraumont

45

예제에서 제공 한 특별한 경우 가장 명확한 코드는 다음과 같습니다.

if (RequiresSpecialEvent(numError))
    fire_special_event();

분명히 이것은 문제를 코드의 다른 영역으로 옮기지 만 이제는이 테스트를 재사용 할 기회가 있습니다. 또한 해결 방법에 대한 추가 옵션이 있습니다. std :: set을 사용할 수 있습니다. 예를 들면 다음과 같습니다.

bool RequiresSpecialEvent(int numError)
{
    return specialSet.find(numError) != specialSet.end();
}

필자는 이것이 이것이 SpecialSpecialEvent의 최상의 구현이라고 제안하는 것이 아니라 옵션 일뿐입니다. 스위치 또는 if-else 체인, 조회 테이블 또는 값에 대한 비트 조작 등을 계속 사용할 수 있습니다. 의사 결정 프로세스가 불분명해질수록 격리 된 기능을 통해 더 많은 가치를 얻을 수 있습니다.


5
이건 정말 진실. 가독성은 스위치와 if 문보다 훨씬 낫습니다. 나는 실제로 이런 식으로 대답하려고했지만 당신은 나를 이겼습니다. :-)
mlarsen 2014 년

열거 형 값이 모두 작은 경우 해시가 필요 없으며 테이블 만 필요합니다. 예를 들어 const std::bitset<MAXERR> specialerror(initializer); 함께 사용하십시오 if (specialerror[numError]) { fire_special_event(); }. 범위 검사를 원하면 범위를 bitset::test(size_t)벗어난 값에서 예외가 발생합니다. ( bitset::operator[]범위 확인하지 않음). cplusplus.com/reference/bitset/bitset/test . 이것은 아마도 switchesp를 구현하는 컴파일러 생성 점프 테이블보다 성능이 뛰어납니다 . 특별하지 않은 경우에는 단일 지점이 아닙니다.
Peter Cordes

@PeterCordes 나는 여전히 테이블을 자체 기능에 넣는 것이 낫다고 주장합니다. 내가 말했듯이, 당신이 그렇게 할 때 열리는 많은 옵션이 있지만, 나는 그것들을 모두 열거하려고하지 않았습니다.
Mark Ransom

@ MarkRansom : 나는 그것을 추상화하는 데 동의하지 않았다. 을 사용하여 샘플 구현을 제공 한 직후 std::set에 나는 이것이 좋지 않은 선택이라고 지적했습니다. gcc는 이미 32 비트에서 비트 맵을 테스트하기 위해 OP의 코드를 이미 컴파일 한 것으로 나타났습니다. godbolt : goo.gl/qjjv0e . gcc 5.2는 if버전에 대해서도이 작업을 수행합니다 . 또한 더 최근의 gcc는 비트를 올바른 위치에 놓고를 사용 bt하기 위해 시프트 대신 비트 테스트 명령 을 1사용 test reg, imm32합니다.
Peter Cordes

비트 맵에 캐시 누락이 없기 때문에이 즉각적으로 일정한 비트 맵은 큰 승리입니다. "특수"오류 코드가 모두 64 이하인 경우 작동합니다. (또는 레거시 32 비트 코드의 경우 32) 컴파일러는 0이 아닌 경우 가장 작은 대소 문자 값을 뺍니다. 테이크 아웃은 최신 컴파일러가 부피가 큰 데이터 구조를 사용하도록 지시하지 않는 한 사용하는 모든 논리에서 좋은 코드를 얻을 수있을 정도로 똑똑하다는 것입니다.
Peter Cordes

24

스위치 더 빠릅니다.

루프 내에서 30 개의 다른 값을 if / else-ing하고 스위치를 사용하여 동일한 코드와 비교하여 스위치가 얼마나 빠른지 확인하십시오.

이제 스위치에는 하나의 실제 문제가 있습니다 . 스위치는 컴파일 할 때 각 케이스 내부의 값을 알아야합니다. 이것은 다음 코드를 의미합니다.

// WON'T COMPILE
extern const int MY_VALUE ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

컴파일하지 않습니다.

대부분의 사람들은 정의 (Aargh!)를 사용하고 다른 사람들은 동일한 컴파일 단위로 상수 변수를 선언하고 정의합니다. 예를 들면 다음과 같습니다.

// WILL COMPILE
const int MY_VALUE = 25 ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

결국 개발자는 "속도 + 선명도"와 "코드 결합"중 하나를 선택해야합니다.

(스위치를 지옥처럼 혼란스럽게 쓸 수는 없습니다 ... 현재보고있는 대부분의 스위치는이 "혼란"범주에 속합니다 ... 그러나 이것은 또 다른 이야기입니다 ...)

2008-09-21 수정 :

bk1e 는 다음과 같이 덧붙였다. " 헤더 파일에서 열거 형으로 상수를 정의하는 것이 이것을 처리하는 또 다른 방법"입니다.

당연하지.

extern 유형의 요점은 소스에서 값을 분리하는 것이 었습니다. 이 값을 매크로, 간단한 const int 선언 또는 열거 형으로 정의하면 값을 인라인하는 부작용이 있습니다. 따라서 정의, 열거 값 또는 const int 값이 변경되면 재 컴파일이 필요합니다. extern 선언은 값이 변경되는 경우 다시 컴파일 할 필요가 없지만 스위치를 사용할 수 없도록합니다. 스위치를 사용 한다는 결론은 스위치 코드와 경우로 사용되는 변수 사이의 연결을 증가시킵니다 . 괜찮 으면 스위치를 사용하십시오. 그렇지 않을 때는 놀랄 일이 아닙니다.

.

2013-01-15 편집 :

Vlad Lazarenko 는 내 답변에 대해 언급하면서 스위치로 생성 된 어셈블리 코드에 대한 심층적 인 연구 링크를 제공했습니다. 매우 깨달음 : http://lazarenko.me/switch/


헤더 파일에서 상수를 열거 형으로 정의하는 것도 이것을 처리하는 또 다른 방법입니다.
bk1e

6
스위치가 항상 빠르지않습니다 .

1
@ 블라드 라자 렌코 : 링크 주셔서 감사합니다! 매우 흥미로운 책이었습니다.
paercebal

1
@AhmedHussein user404725의 링크가 끊어졌습니다. 고맙게도 WayBack Machine : web.archive.org/web/20131111091431/http://lazarenko.me/2013/01/… 에서 찾았습니다 . 실제로 WayBack Machine은 큰 축복이 될 수 있습니다.
잭 지핀

감사합니다. 매우 도움이됩니다
Ahmed Hussein

20

컴파일러는 어쨌든 그것을 최적화 할 것입니다-가장 읽기 쉬운 스위치로 이동하십시오.


3
컴파일러가 if-then-else를 만지지 않을 가능성이 있습니다. 실제로, gcc그렇게하지 않을 것입니다 (그럴만한 이유가 있습니다). Clang은 두 경우 모두 이진 검색으로 최적화합니다. 예를 들어 this를 참조 하십시오 .

7

가독성을위한 스위치입니다. 거대한 의견 진술은 유지하기가 어렵고 내 의견으로는 읽기가 어렵습니다.

ERROR_01 : // 의도적 인 넘어짐

또는

(ERROR_01 == numError) ||

후자는 오류가 발생하기 쉽고 첫 번째보다 더 많은 타이핑 및 서식이 필요합니다.


6

가독성을위한 코드. 성능이 더 좋은 것을 알고 싶다면 최적화와 컴파일러가 다양하고 성능 문제가 사람들이 생각하는 위치에 있기 때문에 프로파일 러를 사용하십시오.


6

스위치를 사용하십시오. 스위치는 프로그래머가 기대하는 것입니다.

나는 사람들에게 편안한 느낌을주기 위해 여분의 케이스 라벨을 넣을 것입니다.
다음 프로그래머가 언어 세부 사항에 대해 불필요한 생각을하지 않아도되기를 원하지 않습니다 (몇 달 안에있을 수도 있습니다).


4

컴파일러는 최적화에 정말 능숙 switch합니다. 최근 gcc는 또한 여러 조건을 최적화하는 데 능숙합니다 if.

나는 godbolt 에 대한 테스트 사례를 만들었습니다 .

case값이 서로 가까이 그룹화, GCC, 그 소리, 그리고 ICC는 값이 특별한 사람 중 하나입니다 있는지 확인하는 비트 맵을 사용하는 모든 스마트 충분합니다.

예를 들어 gcc 5.2 -O3은 switchto (그리고 if매우 유사한 것) 를 컴파일합니다 :

errhandler_switch(errtype):  # gcc 5.2 -O3
    cmpl    $32, %edi
    ja  .L5
    movabsq $4301325442, %rax   # highest set bit is bit 32 (the 33rd bit)
    btq %rdi, %rax
    jc  .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

비트 맵은 즉각적인 데이터이므로 액세스 할 수있는 잠재적 인 데이터 캐시 나 점프 테이블이 없습니다.

gcc 4.9.2 -O3 switch는 비트 맵으로 컴파일 하지만 1U<<errNumbermov / shift로 수행합니다. if버전을 일련의 분기로 컴파일합니다 .

errhandler_switch(errtype):  # gcc 4.9.2 -O3
    leal    -1(%rdi), %ecx
    cmpl    $31, %ecx    # cmpl $32, %edi  wouldn't have to wait an extra cycle for lea's output.
              # However, register read ports are limited on pre-SnB Intel
    ja  .L5
    movl    $1, %eax
    salq    %cl, %rax   # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
    testl   $2150662721, %eax
    jne .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

그것이 1을 빼는 방법에 유의하십시오 errNumber( lea그 연산을 이동과 결합하기 위해). 따라서 비트 맵을 32 비트로 즉시 맞출 수 있으며, movabsq더 많은 명령 바이트를 사용 하는 64 비트 즉시를 피할 수 있습니다.

(기계 코드에서) 더 짧은 순서는 다음과 같습니다.

    cmpl    $32, %edi
    ja  .L5
    mov     $2150662721, %eax
    dec     %edi   # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
    bt     %edi, %eax
    jc  fire_special_event
.L5:
    ret

(사용하지 못하는 jc fire_special_event것은 전적으로 존재 하며 컴파일러 버그 입니다.)

rep ret이전 AMD K8 및 K10 (Bulldozer 이전)의 이점을 위해 분기 대상 및 조건부 분기에 사용됩니다. 'rep ret'은 무엇을 의미합니까? . 그렇지 않으면 오래된 CPU에서 분기 예측이 제대로 작동하지 않습니다.

bt레지스터 인수가있는 (비트 테스트)가 빠릅니다. 1을 errNumber비트 단위 로 왼쪽 이동하는 작업과을 수행하는 작업을 결합 test하지만 여전히 1 사이클 대기 시간이며 단일 인텔 Uop입니다. 너무 많은 CISC 의미로 인해 메모리 인수가 느립니다. "비트 문자열"에 대한 메모리 피연산자를 사용하면 테스트 할 바이트의 주소가 다른 인수 (8로 나눔)를 기반으로 계산됩니다. 메모리 피연산자가 가리키는 1, 2, 4 또는 8 바이트 청크로 제한되지 않습니다.

에서 Agner 안개의 지시 테이블 , 가변 카운트 시프트 명령은보다 느린 bt최근 인텔 (2 마이크로 연산 대신 1, 시프트가의 필요한 것을 다른 모든 일을하지 않음)에.


4

다른 경우에 스위치 ()의 장점 :-1. 개별 진술을 참 또는 거짓 조건으로 확인 해야하는 경우와 달리 각 사례는 이전 사례에 의존하지 않기 때문에 다른 경우에 비해 훨씬 효율적입니다.

  1. 없을 때. 단 하나의 표현에 대한 값들의 경우, 다른 경우에 판단은 두 가지 값, 즉 참 또는 거짓에 기초하기 때문에 다른 경우보다 더 유연하다.

  2. 스위치의 값은 사용자 정의 된 반면, 다른 경우의 값은 제한 조건을 기반으로합니다.

  3. 오류가 발생하면 스위치의 명령문을 쉽게 교차 점검하고 정정 할 수 있으며 if 문이있는 경우에는 확인하기가 상대적으로 어렵습니다.

  4. 스위치 케이스는 훨씬 작고 읽고 이해하기 쉽습니다.


2

IMO 이것은 스위치 폴 스루가 이루어진 완벽한 예입니다.


C #에서는 이것이 가을 사고가 일어나는 유일한 경우입니다. 거기에 좋은 논쟁.
BCS

2

미래에 사례가 그룹화되어있을 가능성이 높은 경우 (둘 이상의 사례가 하나의 결과에 해당하는 경우) 스위치를 읽고 유지 관리하기가 더 쉬울 수 있습니다.


2

그들은 똑같이 잘 작동합니다. 최신 컴파일러의 성능은 거의 같습니다.

더 읽기 쉽고 유연하기 때문에 case 문보다 if 문을 선호합니다. "|| max <min"와 같이 숫자 등식을 기반으로하지 않는 다른 조건을 추가 할 수 있습니다. 그러나 여기에 게시 한 간단한 사례는 실제로 중요하지 않으며 가장 읽기 쉬운 것을 수행하십시오.


2

스위치가 확실히 선호됩니다. 스위치의 케이스 목록을보고 long if 조건을 읽는 것보다 그것이 무엇을하고 있는지 아는 것이 더 쉽습니다.

상태의 복제 if는 눈에 어렵습니다. 다음 중 하나 ==가 작성되었다고 가정하십시오 !=. 알아 차 릴까요? 또는 'numError'의 한 인스턴스가 'nmuError'로 작성된 경우 방금 컴파일 되었습니까?

나는 일반적으로 스위치 대신 다형성을 선호하지만 문맥에 대한 세부 사항이 없으면 말하기가 어렵습니다.

성능과 관련하여 가장 좋은 방법은 프로파일 러를 사용하여 예상했던 것과 유사한 조건에서 응용 프로그램의 성능을 측정하는 것입니다. 그렇지 않으면 잘못된 위치와 잘못된 방법으로 최적화하는 것입니다.


2

스위치 솔루션의 호환성에 동의하지만 IMO 는 스위치를 하이재킹하고 있습니다.
스위치의 목적은 값에 따라 다른 처리를하는 것입니다.
: 당신이 의사 코드에 너 한테 설명해야한다면, 의미, 그것이 무엇을하는 경우 때문에, 당신은을 사용하는 것 이 할 whatever_error 경우 ...
그래서 당신이 언젠가를하지 않을 경우, 각 오류에 대한 특정 코드를 가지고 코드를 변경 , if 사용할 입니다.


2
나는 추락 사고에 동의하지 않는 것과 같은 이유로 동의하지 않습니다. 스위치를 "01,07,0A, 10,15,16 및 20 건의 화재 특별 행사의 경우"라고 읽었습니다. 다른 섹션에는 영향을주지 않습니다. 이것은 각 값에 대해 'case'키워드를 반복하는 C ++ 구문의 결과 일뿐입니다.
MSalters

1

명확성과 컨벤션을 위해 if 문을 선택하지만 일부는 동의하지 않을 것이라고 확신합니다. 결국, 당신은 if어떤 조건을 충족 시키기를 원합니다 ! 한 번의 작업으로 스위치를 갖는 것은 조금 필요하지 않은 것 같습니다.


1

SWITCH를 사용한다고 말합니다. 이렇게하면 다른 결과 만 구현하면됩니다. 10 개의 동일한 사례가 기본값을 사용할 수 있습니다. 변경 사항 하나만 명시 적으로 변경 사항을 구현하는 경우 기본값을 편집 할 필요가 없습니다. IF 및 ELSEIF를 편집하는 것보다 SWITCH에서 사례를 추가하거나 제거하는 것이 훨씬 쉽습니다.

switch(numerror){
    ERROR_20 : { fire_special_event(); } break;
    default : { null; } break;
}

어쩌면 가능성 목록에 대해 조건 (이 경우 numerror)을 테스트 할 수도 있습니다. 어쩌면 결과가 없을 경우 SWITCH가 사용되지 않을 수도 있습니다.


총 약 30 개의 오류가 있습니다. 10은 특별한 조치가 필요하므로 조치가 필요하지 않은 ~ 20 오류에 대한 기본값을 사용하고 있습니다.
Zing-

1

오류 코드가 30 개뿐이므로 점프 테이블을 코딩 한 다음 컴파일러가 올바른 작업을 수행하기를 희망하지 않고 모든 최적화 선택을 직접 수행하십시오 (점프는 항상 가장 빠릅니다). 또한 코드를 매우 작게 만듭니다 (점프 테이블의 정적 선언 제외). 또한 디버거를 사용하면 테이블 데이터를 직접 파킹하여 필요할 때 런타임에 동작을 수정할 수 있다는 부작용이 있습니다.


와우, 그것은 간단한 문제를 복잡한 문제로 바꾸는 방법처럼 보입니다. 컴파일러가 당신을 위해 훌륭한 일을 할 때 왜 그 모든 문제에 가야합니까? 또한 분명히 오류 처리기이므로 속도가 중요하지는 않습니다. 스위치는 지금까지 읽고 유지하는 가장 쉬운 방법입니다.
MrZebra

테이블은 거의 복잡하지 않습니다. 실제로 코드로 전환하는 것보다 간단합니다. 그리고 성명서는 성능이 한 요소라고 언급했습니다.
Greg Whitfield

그것은 조기 최적화처럼 들립니다. 열거 형 값을 작고 연속적으로 유지하는 한 컴파일러에서이를 수행해야합니다. 스위치를 별도의 기능에 넣으면 Mark Ransom이 대답에서 제안한 것처럼 스위치를 사용하는 코드를 작고 작게 유지하면 동일한 작은 코드 이점을 얻을 수 있습니다.
Peter Cordes

당신은 아무것도를 직접 구현하는 거라면 또한,하는을 std::bitset<MAXERR> specialerror;다음 if (specialerror[err]) { special_handler(); }. 이것은 점프 테이블보다 빠릅니다. 그렇지 않은 경우.
Peter Cordes

1

최선의 방법에 대해서는 잘 모르겠지만 스위치를 사용하고 'default'를 통해 의도적 인 넘어짐을 포착합니다.


1

미적으로 나는이 접근법을 선호하는 경향이있다.

unsigned int special_events[] = {
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20
 };
 int special_events_length = sizeof (special_events) / sizeof (unsigned int);

 void process_event(unsigned int numError) {
     for (int i = 0; i < special_events_length; i++) {
         if (numError == special_events[i]) {
             fire_special_event();
             break;
          }
     }
  }

논리를 조금 어둡게 만들 수 있도록 데이터를 조금 더 똑똑하게 만드십시오.

나는 그것이 이상하게 보인다는 것을 안다. 영감은 다음과 같습니다 (파이썬에서 어떻게했는지) :

special_events = [
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20,
    ]
def process_event(numError):
    if numError in special_events:
         fire_special_event()

4
언어의 구문 우리가 솔루션을 구현하는 방법에 영향을 미칩니다 ... => C에서는보기에 좋지 않고 파이썬에서는보기 좋습니다. :)
rlerallut

비트 맵을 사용 하시겠습니까? error_0a가 0x0a 등이면 긴 비트를 비트로 넣을 수 있습니다. long long special_events = 1LL << 1 | 1LL << 7 | 1LL << 0xa ... 그런 다음 if (special_events & (1LL << numError) fire_special_event ()
paperhorse를 사용하십시오.

1
왝. 당신은 (N은 사건 처리의 수입니다) O (N) 최악의 경우에 O를 (1) (점프 테이블이 생성되는 경우) 최악의 경우의 동작을 설정했습니다, 그리고 당신은 사용 break외부 case(예, 미성년자 그럼에도 불구하고 죄). :)
Mac

왝? 그는 성능과 공간이 중요하지 않다고 말했다. 나는 단순히 문제를 보는 다른 방법을 제안하고 있었다. 우리가 인간이 덜 생각하는 방식으로 문제를 나타낼 수 있다면 컴퓨터가 더 많이 생각해야하는지 상관하지 않습니다.
mbac32768

1
while (true) != while (loop)

아마도 첫 번째 것은 컴파일러에 의해 최적화되어 루프 카운트를 증가시킬 때 두 번째 루프가 더 느린 이유를 설명합니다.


이것은 McAnix의 답변에 대한 의견 인 것으로 보입니다. 그것은 Java에서 루프 엔드 조건으로 타이밍 if대 타이밍에서 시도하는 문제 중 하나 일뿐 switch입니다.
Peter Cordes

1

프로그램을 컴파일 할 때 차이점이 있는지 모르겠습니다. 그러나 프로그램 자체와 코드를 가능한 한 간단하게 유지하려면 개인적으로 프로그램이 원하는 것에 달려 있다고 생각합니다. 그렇지 않으면 다른 진술의 장점이 있다면, 다음과 같습니다.

함수 (표준 라이브러리 또는 개인)를 조건부로 사용할 수있는 특정 범위에 대해 변수를 테스트 할 수 있습니다.

(예:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 if( a > 0 && a < 5)
   {
     cout<<"a is between 0, 5\n";

   }else if(a > 5 && a < 10)

     cout<<"a is between 5,10\n";

   }else{

       "a is not an integer, or is not in range 0,10\n";

그러나 다른 방법이 있다면 서두르면 복잡하고 지저분해질 수 있습니다 (최선의 시도에도 불구하고). 스위치 문은 더 명확하고 깔끔하며 읽기 쉬운 경향이 있습니다. 특정 값에 대해서만 테스트 할 수 있습니다 (예 :

`int a;
 cout<<"enter value:\n";
 cin>>a;

 switch(a)
 {
    case 0:
    case 1:
    case 2: 
    case 3:
    case 4:
    case 5:
        cout<<"a is between 0,5 and equals: "<<a<<"\n";
        break;
    //other case statements
    default:
        cout<<"a is not between the range or is not a good value\n"
        break;

나는 if-else if-else 문장을 선호하지만 실제로는 당신에게 달려 있습니다. 함수를 조건으로 사용하거나 범위, 배열 또는 벡터에 대해 무언가를 테스트하거나 복잡한 중첩을 처리하지 않으려면 If else if else block을 사용하는 것이 좋습니다. 단일 값을 테스트하거나 깨끗하고 읽기 쉬운 블록을 원한다면 switch () 케이스 블록을 사용하는 것이 좋습니다.


0

속도와 메모리 사용량에 대해 알려주는 사람은 아니지만 스위치 통계를 보는 것은 큰 if 문을 이해하는 것이 훨씬 쉽습니다 (특히 2-3 개월 줄).


0

나는 그것의 오래된 알고 있지만

public class SwitchTest {
static final int max = 100000;

public static void main(String[] args) {

int counter1 = 0;
long start1 = 0l;
long total1 = 0l;

int counter2 = 0;
long start2 = 0l;
long total2 = 0l;
boolean loop = true;

start1 = System.currentTimeMillis();
while (true) {
  if (counter1 == max) {
    break;
  } else {
    counter1++;
  }
}
total1 = System.currentTimeMillis() - start1;

start2 = System.currentTimeMillis();
while (loop) {
  switch (counter2) {
    case max:
      loop = false;
      break;
    default:
      counter2++;
  }
}
total2 = System.currentTimeMillis() - start2;

System.out.println("While if/else: " + total1 + "ms");
System.out.println("Switch: " + total2 + "ms");
System.out.println("Max Loops: " + max);

System.exit(0);
}
}

루프 수를 변경하면 많은 변화가 있습니다.

if / else : 5ms 스위치 : 1ms 최대 루프 : 100000

if / else : 5ms 스위치 : 3ms 최대 루프 : 1000000

if / else : 5ms 스위치 : 14ms 최대 루프 : 10000000

if / else : 5ms 스위치 : 149ms 최대 루프 : 100000000

(원하는 경우 더 많은 진술을 추가하십시오)


3
좋은 지적이지만, sry, 친구, 당신은 잘못된 언어에 있습니다. 언어의 변화가 다양 함;)
Gabriel Schreiber

2
if(max) break루프는 관계없이 루프 카운트 일정한 시간에 실행? JIT- 컴파일러와 같은 사운드는 루프를로 최적화 할 수있을 정도로 똑똑합니다 counter2=max. currentTimeMillis모든 것이 아직 JIT 컴파일되지 않았기 때문에 첫 번째 호출에 더 많은 오버 헤드가 있으면 스위치보다 속도가 느릴 수 있습니다. 루프를 다른 순서로 넣으면 아마도 다른 결과가 나올 것입니다.
Peter Cordes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.