제목을 8 방향으로 분류 할 때 if / else if 체인을 피하는 방법은 무엇입니까?


111

다음 코드가 있습니다.

if (this->_car.getAbsoluteAngle() <= 30 || this->_car.getAbsoluteAngle() >= 330)
  this->_car.edir = Car::EDirection::RIGHT;
else if (this->_car.getAbsoluteAngle() > 30 && this->_car.getAbsoluteAngle() <= 60)
  this->_car.edir = Car::EDirection::UP_RIGHT;
else if (this->_car.getAbsoluteAngle() > 60 && this->_car.getAbsoluteAngle() <= 120)
  this->_car.edir = Car::EDirection::UP;
else if (this->_car.getAbsoluteAngle() > 120 && this->_car.getAbsoluteAngle() <= 150)
  this->_car.edir = Car::EDirection::UP_LEFT;
else if (this->_car.getAbsoluteAngle() > 150 && this->_car.getAbsoluteAngle() <= 210)
  this->_car.edir = Car::EDirection::LEFT;
else if (this->_car.getAbsoluteAngle() > 210 && this->_car.getAbsoluteAngle() <= 240)
  this->_car.edir = Car::EDirection::DOWN_LEFT;
else if (this->_car.getAbsoluteAngle() > 240 && this->_car.getAbsoluteAngle() <= 300)
  this->_car.edir = Car::EDirection::DOWN;
else if (this->_car.getAbsoluteAngle() > 300 && this->_car.getAbsoluteAngle() <= 330)
  this->_car.edir = Car::EDirection::DOWN_RIGHT;

나는 ifs 사슬 을 피하고 싶다 . 정말 못 생겼어요. 이것을 작성하는 또 다른, 아마도 더 깨끗한 방법이 있습니까?


77
@Oraekiathis->_car.getAbsoluteAngle() 전체 캐스케이드 전에 한 번 fectch하면 훨씬 덜 추하고 입력 하기가 적고 읽기가 더 좋아 보일 것입니다.
πάντα ῥεῖ

26
모든 것을 명시 적으로 역 참조는 this( this->) 필요하지 않습니다 정말 가독성 .. 아무것도 좋은하지 않습니다
예스퍼 Juhl

2
@Neil Pair as key, enum as value, custom lookup lambda.
πάντα ῥεῖ

56
이러한 모든 >테스트 없이는 코드가 훨씬 덜 추악 할 것입니다 . 그것들 각각은 이전 if문장 에서 이미 (반대 방향으로) 테스트 되었기 때문에 필요하지 않습니다 .
Pete Becker

10
@PeteBecker 그것은 이런 코드에 대한 내 애완 동물 중 하나입니다. 너무 많은 프로그래머가 else if.
Barmar

답변:


176
#include <iostream>

enum Direction { UP, UP_RIGHT, RIGHT, DOWN_RIGHT, DOWN, DOWN_LEFT, LEFT, UP_LEFT };

Direction GetDirectionForAngle(int angle)
{
    const Direction slices[] = { RIGHT, UP_RIGHT, UP, UP, UP_LEFT, LEFT, LEFT, DOWN_LEFT, DOWN, DOWN, DOWN_RIGHT, RIGHT };
    return slices[(((angle % 360) + 360) % 360) / 30];
}

int main()
{
    // This is just a test case that covers all the possible directions
    for (int i = 15; i < 360; i += 30)
        std::cout << GetDirectionForAngle(i) << ' ';

    return 0;
}

이것이 내가 할 방법입니다. (내 이전 의견에 따라).


92
나는 말 그대로 내가 잠시 동안 enum 대신 Konami Code를 보았다고 생각했습니다.
Zano

21
@CodesInChaos : C99와 C는 C 번호와 같은 요구 사항이 ++ : 경우에 것을 q = a/b하고 r = a%b다음은 q * b + r동일해야합니다 a. 따라서 C99에서는 나머지가 음수가되는 것이 합법적입니다. BorgLeader, 당신의 문제를 해결할 수 있습니다 (((angle % 360) + 360) % 360) / 30.
Eric Lippert

7
@ericlippert, 당신과 당신의 계산 수학 지식은 계속해서 인상적입니다.
gregsdennis

33
이것은 매우 영리하지만 완전히 읽을 수없고 더 이상 유지 보수가 불가능하기 때문에 원본의 "추악함"에 대한 좋은 해결책이라는 데 동의하지 않습니다. 여기에는 개인적인 취향의 요소가 있지만 x4u 및 motoDrizzt에 의해 정리 된 분기 버전이 훨씬 더 바람직하다는 것을 알았습니다.
IMSoP

4
@cyanbeam 메인의 for 루프는 "데모"일뿐 GetDirectionForAngle입니다. if / else 캐스케이드를 대체하기 위해 제안하는 것입니다. 둘 다 O (1)입니다 ...
Borgleader

71

map::lower_bound지도에서 각 각도의 상한선을 사용 하고 저장할 수 있습니다 .

아래 작업 예 :

#include <cassert>
#include <map>

enum Direction
{
    RIGHT,
    UP_RIGHT,
    UP,
    UP_LEFT,
    LEFT,
    DOWN_LEFT,
    DOWN,
    DOWN_RIGHT
};

using AngleDirMap = std::map<int, Direction>;

AngleDirMap map = {
    { 30, RIGHT },
    { 60, UP_RIGHT },
    { 120, UP },
    { 150, UP_LEFT },
    { 210, LEFT },
    { 240, DOWN_LEFT },
    { 300, DOWN },
    { 330, DOWN_RIGHT },
    { 360, RIGHT }
};

Direction direction(int angle)
{
    assert(angle >= 0 && angle <= 360);

    auto it = map.lower_bound(angle);
    return it->second;
}

int main()
{
    Direction d;

    d = direction(45);
    assert(d == UP_RIGHT);

    d = direction(30);
    assert(d == RIGHT);

    d = direction(360);
    assert(d == RIGHT);

    return 0;
}

부서가 필요하지 않습니다. 좋은!
O. Jones

17
@ O.Jones : 컴파일 시간 상수로 나누는 것은 상당히 저렴합니다. table[angle%360/30]저렴하고 가지가 없기 때문에 답변 중 하나를 선택하겠습니다 . 소스와 유사한 asm으로 컴파일되는 경우 트리 검색 루프보다 훨씬 저렴합니다. ( std::unordered_map일반적으로 해시 테이블이지만 std::map일반적으로 빨간색-검정 이진 트리입니다. 받아 들여지는 답변 angle%360 / 30은 각도에 대한 완벽한 해시 함수로 효과적으로 사용 됩니다 (몇 가지 항목을 복제 한 후 Bijay의 답변은 오프셋으로 피합니다).
Peter Cordes 2017 년

2
lower_bound정렬 된 배열에서 사용할 수 있습니다 . 그것은 map.
wilx

@PeterCordes지도 조회는 작성 및 유지 관리가 쉽습니다. 범위가 변경되면 해싱 코드를 업데이트하면 버그가 발생할 수 있으며 범위가 균일하지 않게되면 단순히 무너질 수 있습니다. 그 코드가 성능에 중요하지 않다면 신경 쓰지 않을 것입니다.
OhJeez

@OhJeez : 이미 균일하지 않은데, 여러 버킷에서 동일한 값을 사용하여 처리됩니다. 아주 작은 제수를 사용하고 너무 많은 버킷을 갖는 것을 의미하지 않는 한 더 작은 제수를 사용하여 더 많은 버킷을 얻으십시오. 또한 성능이 중요하지 않다면 if / else 체인도 나쁘지 않습니다 this->_car.getAbsoluteAngle(). tmp var 로 인수 분해하여 단순화하고 각 OP if()절 에서 중복 비교를 제거하면 (이미 일치했을 항목을 확인) 앞의 if ()). 또는 @ wilx의 정렬 된 배열 제안을 사용하십시오.
Peter Cordes

58

각 요소가 30도 블록과 연결된 배열을 만듭니다.

Car::EDirection dirlist[] = { 
    Car::EDirection::RIGHT, 
    Car::EDirection::UP_RIGHT, 
    Car::EDirection::UP, 
    Car::EDirection::UP, 
    Car::EDirection::UP_LEFT, 
    Car::EDirection::LEFT, 
    Car::EDirection::LEFT, 
    Car::EDirection::DOWN_LEFT,
    Car::EDirection::DOWN, 
    Car::EDirection::DOWN, 
    Car::EDirection::DOWN_RIGHT, 
    Car::EDirection::RIGHT
};

그런 다음 각도 / 30으로 배열을 인덱싱 할 수 있습니다.

this->_car.edir = dirlist[(this->_car.getAbsoluteAngle() % 360) / 30];

비교 나 분기가 필요하지 않습니다.

그러나 결과 는 원본과 약간 다릅니다. 테두리의 값, 즉 30, 60, 120 등은 다음 범주에 배치됩니다. 예를 들어, 원래 코드에서 유효한 값 UP_RIGHT은 31 ~ 60입니다. 위 코드는 30 ~ 59를 할당합니다.UP_RIGHT .

각도에서 1을 빼면이 문제를 해결할 수 있습니다.

this->_car.edir = dirlist[((this->_car.getAbsoluteAngle() - 1) % 360) / 30];

이것은 이제 우리 RIGHT에게 30을줍니다.UP_RIGHT 60 등을 제공합니다.

0의 경우 표현식은 (-1 % 360) / 30. 이것은 유효하기 때문에 -1 % 360 == -1-1 / 30 == 0 우리는 여전히 0의 인덱스를 얻을 수 있도록.

C ++ 표준 의 섹션 5.6 은이 동작을 확인합니다.

4 이항 /연산자는 몫을 산출하고 이항 %연산자는 첫 번째 표현식을 두 번째로 나눈 나머지를 산출합니다. /또는 의 두 번째 피연산자 %가 0이면 동작이 정의되지 않습니다. 정수 피연산자의 경우 /연산자는 분수 부분이 삭제 된 대수 몫을 산출합니다. 몫이 경우 a/b결과의 타입에서 표현할 수이고, (a/b)*b + a%b동일하다 a.

편집하다:

이와 같은 구조의 가독성과 유지 보수성에 대해 많은 질문이 제기되었습니다. motoDrizzt가 제공 한 대답은 유지 관리가 더 쉽고 "추악"하지 않은 원래 구조를 단순화하는 좋은 예입니다.

그의 대답을 확장하면 삼항 연산자를 사용하는 또 다른 예가 있습니다. 원본 게시물의 각 케이스가 동일한 변수에 할당되므로이 연산자를 사용하면 가독성을 더욱 높일 수 있습니다.

int angle = ((this->_car.getAbsoluteAngle() % 360) + 360) % 360;

this->_car.edir = (angle <= 30)  ?  Car::EDirection::RIGHT :
                  (angle <= 60)  ?  Car::EDirection::UP_RIGHT :
                  (angle <= 120) ?  Car::EDirection::UP :
                  (angle <= 150) ?  Car::EDirection::UP_LEFT :
                  (angle <= 210) ?  Car::EDirection::LEFT : 
                  (angle <= 240) ?  Car::EDirection::DOWN_LEFT :
                  (angle <= 300) ?  Car::EDirection::DOWN:  
                  (angle <= 330) ?  Car::EDirection::DOWN_RIGHT :
                                    Car::EDirection::RIGHT;

49

그 코드는 추악하지 않고 간단하고 실용적이며 읽기 쉽고 이해하기 쉽습니다. 그것은 자신의 방법으로 분리 될 것이므로 일상 생활에서 아무도 그것을 다룰 필요가 없을 것입니다. 그리고 누군가가 그것을 확인해야하는 경우에-아마도 그가 다른 곳에서 문제를 위해 당신의 응용 프로그램을 디버깅하고 있기 때문일 것입니다-코드와 그것이하는 일을 이해하는 데 2 ​​초가 걸릴 것입니다.

내가 그런 디버그를하고 있었다면 당신의 함수가 무엇을하는지 이해하려고 5 분을 할애하지 않아도 돼 기쁘다. 이와 관련하여 다른 모든 기능은 디버깅 할 때 사람들이 심도있게 분석하고 테스트해야하는 복잡한 혼란 속에서 간단하고 잊어 버리는 버그없는 루틴을 변경하므로 완전히 실패합니다. 프로젝트 관리자로서 저는 개발자가 간단한 작업을 수행하고이를 단순하고 무해한 방식으로 구현하는 대신 지나치게 복잡한 방식으로 구현하는 데 시간을 낭비하는 것에 크게 화가났습니다. 당신이 그것에 대해 생각하고, 그렇게 묻고, 그 일의 유지 보수와 가독성을 악화시키기 위해 낭비되는 모든 시간을 생각하십시오.

즉, 코드의 가독성을 떨어 뜨리는 일반적인 실수와 매우 쉽게 수행 할 수있는 몇 가지 개선 사항이 있습니다.

int angle = this->_car.getAbsoluteAngle();

if (angle <= 30 || angle >= 330)
  return Car::EDirection::RIGHT;
else if (angle <= 60)
  return Car::EDirection::UP_RIGHT;
else if (angle <= 120)
  return Car::EDirection::UP;
else if (angle <= 150)
  return Car::EDirection::UP_LEFT;
else if (angle <= 210)
  return Car::EDirection::LEFT;
else if (angle <= 240)
  return Car::EDirection::DOWN_LEFT;
else if (angle <= 300)
  return Car::EDirection::DOWN;
else if (angle <= 330)
  return Car::EDirection::DOWN_RIGHT;

이것을 메서드에 넣고 반환 된 값을 객체에 할당하고 메서드를 축소 한 다음 남은 영원 동안 잊어 버리십시오.

추신 : 330 임계 값을 초과하는 또 다른 버그가 있지만 어떻게 처리하고 싶은지 모르겠 기 때문에 전혀 수정하지 않았습니다.


나중에 업데이트

의견에 따라 다음과 같은 경우 else를 제거 할 수도 있습니다.

int angle = this->_car.getAbsoluteAngle();

if (angle <= 30 || angle >= 330)
  return Car::EDirection::RIGHT;

if (angle <= 60)
  return Car::EDirection::UP_RIGHT;

if (angle <= 120)
  return Car::EDirection::UP;

if (angle <= 150)
  return Car::EDirection::UP_LEFT;

if (angle <= 210)
  return Car::EDirection::LEFT;

if (angle <= 240)
  return Car::EDirection::DOWN_LEFT;

if (angle <= 300)
  return Car::EDirection::DOWN;

if (angle <= 330)
  return Car::EDirection::DOWN_RIGHT;

나는 특정 지점이 자신의 선호도의 문제가된다고 느끼기 때문에 그렇게하지 않았습니다. 제 대답의 범위는 "코드의 추함"에 대한 우려에 대해 다른 관점을 제공하는 것이 었습니다. 어쨌든 내가 말했듯이 누군가 댓글에서 지적했고 그것을 보여주는 것이 합리적이라고 생각합니다.


1
도대체 그게 무엇을 성취해야 했습니까?
추상화가 전부입니다.

8
이 경로를 이동하려는 경우, 당신은 적어도 불필요한 없애한다 else if, if충분합니다.
Ðаn

10
@ Ðаn 나는 else if. 코드 블록을 한 눈에 볼 수 있고 관련없는 문 그룹이 아니라 의사 결정 트리임을 확인할 수 있으면 유용합니다. 예, else또는. 이후 의 컴파일러break 에는 필요하지 않지만 코드를 살펴 보는 사람에게는 유용합니다. return
IMSoP

@ Ðаn 거기에 추가 중첩이 필요한 언어는 본 적이 없습니다. 별도의 elseif/ elsif키워드가 있거나 if여기처럼 시작되는 하나의 문 블록을 기술적으로 사용하고 있습니다. 내가 생각하는 것과 내가 생각하는 것에 대한 간단한 예
IMSoP

7
@ Ðаn 예, 끔찍할 것이라는 데 동의합니다. 그러나 그것은 else당신이 그렇게 만드는 것이 else if아니라, 뚜렷한 진술로 인식하지 않는 형편없는 스타일 가이드입니다 . 나는 항상 중괄호를 사용하지만, 요점에서 보여준 것처럼 그런 코드를 결코 작성하지 않을 것입니다.
IMSoP

39

의사 코드에서 :

angle = (angle + 30) %360; // Offset by 30. 

그래서, 우리가 0-60, 60-90, 90-150, ... 범주로. 90 도인 각 사분면에서 한 부분은 60이고 한 부분은 30입니다. 이제 다음과 같습니다.

i = angle / 90; // Figure out the quadrant. Could be 0, 1, 2, 3 

j = (angle - 90 * i) >= 60? 1: 0; // In the quardrant is it perfect (eg: RIGHT) or imperfect (eg: UP_RIGHT)?

index = i * 2 + j;

적절한 순서로 열거 형을 포함하는 배열의 인덱스를 사용하십시오.


7
이것은 아마도 여기에서 가장 좋은 대답 일 것입니다. 원래 질문자가 나중에 열거 형의 사용을 살펴보면 그가 단지 숫자로 다시 변환되는 경우가 있다는 것을 알게 될 것입니다! 열거 형을 완전히 제거하고 방향 정수를 고수하는 것은 아마도 그의 코드의 다른 위치에서 의미가 있으며이 답변은 바로 거기에 도착합니다.
Bill K

18
switch (this->_car.getAbsoluteAngle() / 30) // integer division
{
    case 0:
    case 11: this->_car.edir = Car::EDirection::RIGHT; break;
    case 1: this->_car.edir = Car::EDirection::UP_RIGHT; break;
    ...
    case 10: this->_car.edir = Car::EDirection::DOWN_RIGHT; break;
}

각도 = this-> _ car.getAbsoluteAngle (); 섹터 = (각도 % 360) / 30; 결과는 12 개 섹터입니다. 그런 다음 배열로 인덱싱하거나 위와 같이 스위치 / 케이스를 사용합니다. 컴파일러는 어쨌든 점프 테이블로 변환합니다.
ChuckCottrill

1
if / else 체인보다 더 나은 것은 아닙니다.
Bill K

5
@BillK : 컴파일러가이를 테이블 조회로 바꾸면 그럴 수도 있습니다. if / else 체인보다 가능성이 높습니다. 그러나 쉽고 아키텍처 별 트릭이 필요하지 않기 때문에 소스 코드에서 테이블 조회를 작성하는 것이 가장 좋습니다.
Peter Cordes 2017 년

일반적으로 성능은 문제가되지 않아야합니다. 가독성과 유지 보수성입니다. 모든 스위치 및 if / else 체인은 일반적으로 새 항목을 추가 할 때마다 여러 위치에서 업데이트해야하는 복잡한 복사 및 붙여 넣기 코드를 의미합니다. 두 가지를 모두 피하고 디스패치 테이블, 계산을 시도하거나 파일에서 데이터를로드하고 가능한 경우 데이터로 처리하는 것이 가장 좋습니다.
Bill K

PeterCordes 컴파일러는 LUT에 대해 스위치와 동일한 코드를 생성 할 가능성이 높습니다. @BillK 당신은 스위치를 0..12-> Car :: EDirection 함수로 추출 할 수 있습니다. 이것은 LUT와 동등한 반복을 가질 것입니다
Caleth

16

첫 번째 if는 특별한 경우를 무시 하고 나머지는 모두 동일한 패턴을 따릅니다 : 최소, 최대 및 방향; 의사 코드 :

if (angle > min && angle <= max)
  _car.edir = direction;

이 실제 C ++를 만드는 것은 다음과 같습니다.

enum class EDirection {  NONE,
   RIGHT, UP_RIGHT, UP, UP_LEFT, LEFT, DOWN_LEFT, DOWN, DOWN_RIGHT };

struct AngleRange
{
    int min, max;
    EDirection direction;
};

이제 여러 개의 ifs를 작성하는 대신 다양한 가능성을 반복하십시오.

EDirection direction_from_angle(int angle, const std::vector<AngleRange>& angleRanges)
{
    for (auto&& angleRange : angleRanges)
    {
        if ((angle > angleRange.min) && (angle <= angleRange.max))
            return angleRange.direction;
    }

    return EDirection::NONE;
}

( throwing 대신 예외 return를 사용하는 NONE것은 또 다른 옵션입니다).

그러면 다음을 호출합니다.

_car.edir = direction_from_angle(_car.getAbsoluteAngle(), {
    {30, 60, EDirection::UP_RIGHT},
    {60, 120, EDirection::UP},
    // ... etc.
});

이 기술을 데이터 기반 프로그래밍이라고 합니다. 많은 ifs를 제거하는 것 외에도 다른 코드를 다시 작업하지 않고도 쉽게 더 많은 방향 (예 : NNW)을 추가하거나 숫자 (왼쪽, 오른쪽, 위, 아래)를 줄일 수 있습니다.


(첫 번째 특별한 경우를 처리하는 것은 "독자를위한 연습"으로 남겨집니다. :-))


1
기술적으로 모든 각도 범위가 일치하면 최소값을 제거 할 수 있습니다. 그러면 if(angle <= angleRange.max).NET과 같은 C ++ 11 기능을 사용 하는 경우 조건이 +1로 줄어들 것 enum class입니다.
Pharap

12

에 대한 룩업 테이블을 기반으로 제안 된 변형 angle / 30이 선호 될 수 있지만 여기에는 비교 횟수를 최소화하기 위해 하드 코딩 된 이진 검색을 사용하는 대안이 있습니다.

static Car::EDirection directionFromAngle( int angle )
{
    if( angle <= 210 )
    {
        if( angle > 120 )
            return angle > 150 ? Car::EDirection::LEFT
                               : Car::EDirection::UP_LEFT;
        if( angle > 30 )
            return angle > 60 ? Car::EDirection::UP
                              : Car::EDirection::UP_RIGHT;
    }
    else // > 210
    {
        if( angle <= 300 )
            return angle > 240 ? Car::EDirection::DOWN
                               : Car::EDirection::DOWN_LEFT;
        if( angle <= 330 )
            return Car::EDirection::DOWN_RIGHT;
    }
    return Car::EDirection::RIGHT; // <= 30 || > 330
}

2

정말로 중복을 피하고 싶다면 이것을 수학 공식으로 표현할 수 있습니다.

우선 @Geek의 Enum을 사용하고 있다고 가정합니다.

Enum EDirection { RIGHT =0, UP_RIGHT, UP, UP_LEFT, LEFT, DOWN_LEFT,DOWN, DOWN_RIGHT}

이제 정수 수학을 사용하여 열거 형을 계산할 수 있습니다 (배열 필요 없음).

EDirection angle2dir(int angle) {
    int d = ( ((angle%360)+360)%360-1)/30;
    d-=d/3; //some directions cover a 60 degree arc
    d%=8;
    //printf ("assert(angle2dir(%3d)==%-10s);\n",angle,dir2str[d]);
    return (EDirection) d;
}

@motoDrizzt가 지적했듯이 간결한 코드가 반드시 읽을 수있는 코드는 아닙니다. 그것을 수학으로 표현하면 어떤 방향이 더 넓은 호를 커버한다는 것을 명백하게한다는 작은 이점이 있습니다. 이 방향으로 가고 싶다면 코드를 이해하는 데 도움이되는 몇 가지 어설 션을 추가 할 수 있습니다.

assert(angle2dir(  0)==RIGHT     ); assert(angle2dir( 30)==RIGHT     );
assert(angle2dir( 31)==UP_RIGHT  ); assert(angle2dir( 60)==UP_RIGHT  );
assert(angle2dir( 61)==UP        ); assert(angle2dir(120)==UP        );
assert(angle2dir(121)==UP_LEFT   ); assert(angle2dir(150)==UP_LEFT   );
assert(angle2dir(151)==LEFT      ); assert(angle2dir(210)==LEFT      );
assert(angle2dir(211)==DOWN_LEFT ); assert(angle2dir(240)==DOWN_LEFT );
assert(angle2dir(241)==DOWN      ); assert(angle2dir(300)==DOWN      );
assert(angle2dir(301)==DOWN_RIGHT); assert(angle2dir(330)==DOWN_RIGHT);
assert(angle2dir(331)==RIGHT     ); assert(angle2dir(360)==RIGHT     );

어설 션을 추가하면 중복을 추가했지만 어설 션의 중복은 그렇게 나쁘지 않습니다. 일관성없는 주장이 있으면 곧 알게 될 것입니다. 어설 션은 배포하는 실행 파일을 부 풀리지 않도록 릴리스 버전에서 컴파일 할 수 있습니다. 그럼에도 불구하고이 접근 방식은 코드를 덜보기 흉하게 만드는 것보다 최적화하려는 경우 가장 적합 할 것입니다.


1

파티에 늦었지만 열거 형 플래그와 범위 검사를 사용하여 깔끔한 작업을 수행 할 수 있습니다.

enum EDirection {
    RIGHT =  0x01,
    LEFT  =  0x02,
    UP    =  0x04,
    DOWN  =  0x08,
    DOWN_RIGHT = DOWN | RIGHT,
    DOWN_LEFT = DOWN | LEFT,
    UP_RIGHT = UP | RIGHT,
    UP_LEFT = UP | LEFT,

    // just so we be clear, these won't have much use though
    IMPOSSIBLE_H = RIGHT | LEFT, 
    IMPOSSIBLE_V = UP | DOWN
};

검사 (의사 코드), 각도가 절대적이라고 가정합니다 (0에서 360 사이) :

int up    = (angle >   30 && angle <  150) * EDirection.UP;
int down  = (angle >  210 && angle <  330) * EDirection.DOWN;
int right = (angle <=  60 || angle >= 330) * EDirection.Right;
int left  = (angle >= 120 && angle <= 240) * EDirection.LEFT;

EDirection direction = (Direction)(up | down | right | left);

switch(direction){
    case RIGHT:
         // do right
         break;
    case UP_RIGHT:
         // be honest
         break;
    case UP:
         // whats up
         break;
    case UP_LEFT:
         // do you even left
         break;
    case LEFT:
         // 5 done 3 to go
         break;
    case DOWN_LEFT:
         // your're driving me to a corner here
         break;
    case DOWN:
         // :(
         break;
    case DOWN_RIGHT:
         // completion
         break;

    // hey, we mustn't let these slide
    case IMPOSSIBLE_H:
    case IMPOSSIBLE_V:
        // treat your impossible case here!
        break;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.