यदि 8 दिशाओं में किसी शीर्षक को वर्गीकृत करते समय अगर / तो चेन से बचने के लिए कैसे?


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;

मैं ifएस श्रृंखला से बचना चाहता हूं ; यह वास्तव में बदसूरत है। वहाँ एक और, संभवतः क्लीनर, यह लिखने का तरीका है?


77
@ ओरकिया यह बहुत कम बदसूरत दिखती है, टाइप करने के लिए कम और पढ़ने के लिए बेहतर है यदि आप this->_car.getAbsoluteAngle()पूरे कैस्केड से पहले एक बार फेकते हैं ।
atν 15:α '25

26
सभी की स्पष्ट रूप से this( this->) की जरूरत नहीं है और वास्तव में पठनीयता के लिए कुछ भी अच्छा नहीं करता है ..
जेस्पर जुहल

2
@Neil जोड़ी कुंजी के रूप में, मान के रूप में enum, कस्टम लुकअप लैम्ब्डा।
atν 15:α '25

56
उन सभी >परीक्षणों के बिना कोड बहुत कम बदसूरत होगा ; उनकी जरूरत नहीं है, क्योंकि उनमें से प्रत्येक को पहले से ही (विपरीत दिशा में) पिछले ifबयान में परीक्षण किया गया है ।
पीट बेकर

10
@PeteBecker यह कोड के बारे में मेरे पालतू जानवरों में से एक है। बहुत सारे प्रोग्रामर समझ नहीं पाते हैं else if
बरमार

जवाबों:


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
मैंने शाब्दिक रूप से सोचा था कि मैंने कोनमी कोड को एनम के स्थान पर एक सेकंड के लिए देखा।
ज़ेनो

21
@CodesInChaos: C99 और C ++ की C # जैसी ही आवश्यकता है: अगर q = a/bऔर r = a%bफिर q * b + rबराबर होना चाहिए a। इस प्रकार यह शेष नकारात्मक होने के लिए C99 में कानूनी है। BorgLeader, आप के साथ समस्या को ठीक कर सकते हैं (((angle % 360) + 360) % 360) / 30
एरिक लिपर्ट

7
@ericlippert, आप और आपके कम्प्यूटेशनल गणित के ज्ञान को प्रभावित करते रहते हैं।
gregsdennis

33
यह बहुत चालाक है, लेकिन यह पूरी तरह से अपठनीय है, और इसे बनाए रखने की संभावना नहीं है, इसलिए मैं असहमत हूं कि यह मूल के कथित "कुरूपता" का एक अच्छा समाधान है। व्यक्तिगत स्वाद का एक तत्व है, यहाँ, मुझे लगता है, लेकिन मुझे x4u और motoDrizzt द्वारा साफ किए गए ब्रंचिंग संस्करण बहुत पसंद हैं।
IMSoP

4
@cyanbeam फॉर लूप इन मेन, सिर्फ एक "डेमो" है, GetDirectionForAngleक्या मैं अगर / और कैस्केड के प्रतिस्थापन के रूप में प्रस्ताव कर रहा हूं, वे दोनों 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;
}

किसी विभाजन की आवश्यकता नहीं है। अच्छा!
ओ जोन्स

17
@ O.Jones: संकलित-समय स्थिरांक द्वारा विभाजन काफी सस्ता है, बस एक गुणा और कुछ बदलाव है। मैं एक table[angle%360/30]जवाब के साथ जाऊंगा क्योंकि यह सस्ता और शाखाहीन है। एक पेड़-खोज लूप की तुलना में बहुत सस्ता है, अगर यह स्रोत के समान आसम को संकलित करता है। ( std::unordered_mapआम तौर पर एक हैश तालिका होती है, लेकिन std::mapआम तौर पर एक लाल-काले बाइनरी ट्री होता है। स्वीकृत उत्तर angle%360 / 30कोणों के लिए एक आदर्श हैश फ़ंक्शन के रूप में प्रभावी रूप से उपयोग करता है (युगल प्रविष्टियों की प्रतिकृति के बाद, और बिजय का उत्तर भी एक ऑफसेट के साथ बचता है)।
पीटर कॉर्डेस

2
आप lower_boundएक क्रमबद्ध सरणी पर उपयोग कर सकते हैं । यह एक की तुलना में बहुत अधिक कुशल होगा map
wilx

@PeterCordes मैप लुकिंग लिखना आसान है और बनाए रखना आसान है। यदि रेंज में बदलाव होता है, तो हैशिंग कोड को अपडेट करने से कीड़े हो सकते हैं और यदि रेंज गैर-समान हो जाती हैं, तो यह आसानी से अलग हो सकता है। जब तक वह कोड महत्वपूर्ण नहीं होता, मैं परेशान नहीं होता।
ओहिजे

@ ओहिज्ज़: वे पहले से ही गैर-समान हैं, जो कई बाल्टियों में समान मूल्य रखते हैं। बस अधिक बाल्टी प्राप्त करने के लिए एक छोटे विभाजक का उपयोग करें, जब तक कि इसका अर्थ बहुत छोटे भाजक का उपयोग न करें और बहुत अधिक बाल्टी हो। इसके अलावा, अगर प्रदर्शन से कोई फर्क नहीं पड़ता है, तो एक / / अन्य श्रृंखला खराब नहीं है, अगर this->_car.getAbsoluteAngle()एक tmp var के साथ फैक्टरिंग द्वारा सरलीकृत किया जाता है , और प्रत्येक OP के if()खंड से अनावश्यक तुलना को हटा दिया जाता है (कुछ के लिए जाँच की जाती है जो पहले से ही मेल खाता होगा पूर्ववर्ती अगर ()। या @ wilx के सॉर्ट-ऐरे सुझाव का उपयोग करें।
पीटर कॉर्डेस

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_RIGHT31 से 60 हैं। उपरोक्त कोड 30 से 59 तक प्रदान करता है UP_RIGHT

हम कोण से 1 घटाकर इसके चारों ओर प्राप्त कर सकते हैं:

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

यह अब हमें RIGHT30 के लिए, UP_RIGHT60 के लिए, आदि देता है ।

0 के मामले में, अभिव्यक्ति बन जाती है (-1 % 360) / 30। यह मान्य है क्योंकि -1 % 360 == -1और -1 / 30 == 0, इसलिए हम अभी भी 0 का एक सूचकांक प्राप्त करते हैं।

C ++ मानक की धारा 5.6 इस व्यवहार की पुष्टि करती है:

4 बाइनरी /ऑपरेटर भागफल की पैदावार करता है, और बाइनरी %ऑपरेटर दूसरी द्वारा पहली अभिव्यक्ति के विभाजन से शेष पैदावार देता है। तो के दूसरे संकार्य /या %शून्य है व्यवहार अपरिभाषित है। अभिन्न ऑपरेंड के लिए /ऑपरेटर किसी भी आंशिक भाग को त्यागने के साथ बीजीय भागफल देता है। यदि भागफल 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

यह कोड बदसूरत नहीं है, यह सरल, व्यावहारिक, पठनीय और समझने में आसान है। यह खुद की विधि में अलग-थलग होगा, इसलिए किसी को भी रोजमर्रा की जिंदगी में इससे निपटना नहीं होगा। और सिर्फ अगर किसी को इसकी जाँच करनी है -मायबे क्योंकि वह एक समस्या के लिए आपके आवेदन को कहीं और डिबग कर रहा है-तो यह इतना आसान है कि उसे कोड को समझने में दो सेकंड लगेंगे और यह क्या करता है।

अगर मैं ऐसा डिबग कर रहा था, तो मुझे खुशी होगी कि आपके कार्य को समझने के लिए पांच मिनट खर्च करने की ज़रूरत नहीं है। इस संबंध में, अन्य सभी कार्य पूरी तरह से विफल हो जाते हैं, क्योंकि वे एक सरल, भूल-चूक के बारे में बदल देते हैं, यह एक नियमित गड़बड़ है, एक जटिल गड़बड़ है कि जब लोग डीबगिंग को गहराई से विश्लेषण और परीक्षण करने के लिए मजबूर होंगे। एक प्रोजेक्ट मैनेजर के रूप में, मैं एक डेवलपर को एक सरल कार्य करने के लिए दृढ़ता से परेशान करूंगा और इसे एक सरल, हानिरहित तरीके से लागू करने के बजाय, इसे एक जटिल तरीके से लागू करने के लिए समय बर्बाद करता हूं। जरा सोचिए जब आप इसके बारे में सोच रहे थे, तब एसओ पूछ रहे थे, और सभी बात को बनाए रखने और रखरखाव की खराब स्थिति के कारण।

उस ने कहा, आपके कोड में एक सामान्य गलती है जो इसे काफी कम पठनीय बनाती है, और कुछ सुधार जो आप आसानी से कर सकते हैं:

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;

इसे एक विधि में रखें, वस्तु को लौटाया गया मान निर्दिष्ट करें, विधि को ध्वस्त करें, और बाकी अनंत काल के लिए इसे भूल जाएं।

PS वहाँ 330 दहलीज पर एक और बग है, लेकिन मुझे नहीं पता कि आप इसका इलाज कैसे करना चाहते हैं, इसलिए मैंने इसे ठीक नहीं किया।


बाद में अपडेट करें

टिप्पणी के अनुसार, आप सभी से छुटकारा पा सकते हैं यदि:

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पर्याप्त है।
Ð

10
@ .Аn मैं पूरी तरह से असहमत हूं else if। मुझे लगता है कि यह एक कोड ब्लॉक पर नज़र रखने में सक्षम होने के लिए उपयोगी है, और देखें कि यह असंबंधित बयानों के एक समूह के बजाय एक निर्णय पेड़ है। हाँ, elseया एक के बाद संकलक के लिएbreak आवश्यक नहीं हैं , लेकिन वे उस मानव के लिए उपयोगी हैं जो कोड पर नज़र रखता है। return
IMSoP

@ मैंने कभी भी ऐसी भाषा नहीं देखी, जहाँ किसी अतिरिक्त घोंसले की आवश्यकता होगी। या तो एक अलग elseif/ elsifकीवर्ड है, या आप तकनीकी रूप से एक स्टेटमेंट ब्लॉक का उपयोग कर रहे हैं जो कि ifयहां शुरू होता है। इस बात का त्वरित उदाहरण कि मुझे क्या लगता है कि आप क्या सोच रहे हैं, और मैं क्या सोच रहा हूँ: gist.github.com/IMSoP/90bc1e9e2c56d8314413d7347e76532a
IMSoC

7
@ हाँ, मैं मानता हूँ कि भयानक होगा। लेकिन यह 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
यह अच्छा है, शायद यहाँ सबसे अच्छा जवाब है। संभावना है कि अगर मूल प्रश्नकर्ता ने एनम के उपयोग के बारे में देखा, तो बाद में उसने पाया कि उसके पास एक ऐसा मामला है जहां इसे सिर्फ एक नंबर में बदला जा रहा है! पूरी तरह से और केवल एक दिशा पूर्णांक के साथ चिपके हुए एनम को खत्म करने से संभवतः उनके कोड में अन्य स्थानों के साथ समझ में आता है और यह उत्तर आपको सीधे वहां मिलता है।
बिल के

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;
}

कोण = यह -> _ car.getAbsoluteAngle (); सेक्टर = (कोण% 360) / 30; रिजल्ट 12 सेक्टर हैं। फिर ऐरे में इंडेक्स, या ऊपर के रूप में स्विच / केस का उपयोग करें - जो कंपाइलर वैसे भी जंप टेबल में बदल जाता है।
च्च्कॉट्रिल

1
स्विच वास्तव में बेहतर नहीं अगर / अन्यथा जंजीरों से।
बिल K

5
@ बाइल: यह हो सकता है, यदि कंपाइलर इसे आपके लिए टेबल लुकअप में बदल दे। अगर आई / चेन से अधिक संभावना है। लेकिन चूंकि यह आसान है और किसी भी आर्किटेक्चर-विशिष्ट ट्रिक्स की आवश्यकता नहीं है, इसलिए संभवतः स्रोत कोड में तालिका-लुकअप लिखना सबसे अच्छा है।
पीटर कॉर्डेस

आम तौर पर प्रदर्शन की चिंता नहीं होनी चाहिए - यह पठनीयता और स्थिरता है - प्रत्येक स्विच और यदि / अन्यथा श्रृंखला का मतलब आमतौर पर गन्दा कॉपी और पेस्ट कोड का एक गुच्छा होता है जिसे जब भी आप एक नया आइटम जोड़ते हैं, तो कई स्थानों पर अपडेट करना होगा। दोनों से बचने के लिए और डिस्पैच टेबल, गणना या बस किसी फ़ाइल से डेटा लोड करने और इसे डेटा के रूप में मानने का प्रयास करें।
बिल के

पीटरकॉर्ड्स संकलक स्विच के लिए LUT के समान कोड उत्पन्न करने की संभावना है। @BillK आप स्विच को 0..12 -> कार :: 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;
};

अब, ifएस का एक गुच्छा लिखने के बजाय , बस अपने विभिन्न अधिभोगों पर लूप करें:

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;
}

( throwआईएनजी एक अपवाद के बजाय returnआईएनजी NONEएक और विकल्प है)।

जिसे आप तब कहेंगे:

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

इस तकनीक को डेटा-संचालित प्रोग्रामिंग के रूप में जाना जाता है । एस के एक समूह से छुटकारा पाने के अलावा if, यह आपको आसानी से अधिक दिशाओं (जैसे, एनएनडब्ल्यू) का उपयोग करने या अन्य कोड को फिर से काम किए बिना संख्या (बाएं, दाएं, ऊपर, नीचे) को कम करने की अनुमति देगा।


(अपने पहले विशेष मामले को संभालना "पाठक के लिए एक अभ्यास" के रूप में छोड़ा गया है। :-))


1
तकनीकी रूप से आप दिए गए मिनट को समाप्त कर सकते हैं कि सभी कोण सीमाएं मेल खाती हैं, जो if(angle <= angleRange.max)सी + + 11 जैसी सुविधाओं का उपयोग करने के लिए शर्त को कम कर देगा लेकिन +1 enum class
छपरा

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     );

आपके द्वारा दोहराव जोड़ दिए जाने के बाद, लेकिन जोड़ियों में दोहराव इतना बुरा नहीं है। यदि आपके पास एक असंगत जोर है तो आप जल्द ही पता लगा लेंगे। Asserts को रिलीज़ संस्करण से संकलित किया जा सकता है ताकि आपके द्वारा वितरित निष्पादन योग्य को ब्लोट न किया जा सके। फिर भी, यह दृष्टिकोण संभवतः सबसे अधिक लागू होता है यदि आप कोड को अनुकूलित करना चाहते हैं बजाय इसके कि इसे कम बदसूरत बना दें।


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;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.