การป้อนองค์ประกอบประเภทที่ไม่รู้จักลงในเวกเตอร์

ฉันกำลังทำงานกับโปรแกรมที่รับองค์ประกอบจากผู้ใช้มาจัดเรียง สำหรับโปรแกรมนี้ ฉันต้องใช้เวกเตอร์เนื่องจากไม่ทราบขนาดของรายการองค์ประกอบก่อนที่ผู้ใช้จะป้อนข้อมูล คำแนะนำของเราคือ:

เขียนโปรแกรมในภาษา C++ เพื่อทำการเรียงลำดับรายการองค์ประกอบ องค์ประกอบสามารถเป็นประเภทใดก็ได้ แต่ทั้งหมดจะเป็นประเภทเดียวกัน เช่น จำนวนเต็มทั้งหมด หรือทศนิยมทั้งหมด หรือตัวอักษรทั้งหมด หรือสตริงทั้งหมด (สตริงจะต้องเรียงลำดับเหมือนในพจนานุกรม) คุณสามารถใช้อัลกอริธึมการเรียงลำดับใดก็ได้ตามที่คุณต้องการ

  1. ถามผู้ใช้ว่ามีองค์ประกอบกี่รายการ
  2. ขอให้ผู้ใช้ป้อนองค์ประกอบ
  3. ขอให้ผู้ใช้เลือกลำดับการเรียงลำดับ: จากน้อยไปมากหรือจากมากไปน้อยหรือทั้งสองอย่าง
  4. พิมพ์รายการอินพุตและเอาต์พุต
  5. ผู้ใช้จะไม่ให้ข้อมูลใดๆ เกี่ยวกับประเภทขององค์ประกอบ

ฉันไม่คุ้นเคยกับเวกเตอร์มากนัก (โดยพื้นฐานแล้วครูจะท่องหัวข้อในชั้นเรียน) และหนังสือของฉันก็ไม่ได้ให้ข้อมูลมากมายเกี่ยวกับเรื่องนี้ ปัญหาที่ฉันพบคือฉันไม่รู้ประเภทของรายการองค์ประกอบจนกว่าผู้ใช้จะเริ่มป้อนข้อมูล จนถึงตอนนี้ฉันได้ลองแล้ว:

  • การสร้างเวกเตอร์ประเภทโมฆะ (เห็นได้ชัดว่าไม่ได้รับอนุญาตตอนนี้เมื่อฉันค้นคว้าแล้ว โอ๊ะโอ)
  • โอเวอร์โหลดฟังก์ชันที่เรียกว่า insertInVector โดยการส่งองค์ประกอบแรกไปยังฟังก์ชัน และปล่อยให้ฟังก์ชันกำหนดประเภทเวกเตอร์ที่จะสร้างตามประเภทขององค์ประกอบแรก (ซึ่งดูเหมือนเป็นตัวเลือกที่ดีที่สุดของฉันเมื่อฉันคิดถึงมัน ยกเว้นว่าฉันต้องเข้าถึง vector หลังจากฟังก์ชันยุติลง ดังนั้นสุดท้ายก็ไม่ต้องดำเนินการเช่นกัน)
  • #include <typeinfo> ในโปรแกรม ค้นหาประเภทขององค์ประกอบแรก จากนั้นสร้างเวกเตอร์โดยใช้ vector<typeid(firstElement).name()> และจริงๆ แล้วฉันไม่แน่ใจว่าทำไมมันถึงใช้งานไม่ได้ แต่มันก็ไม่ได้ผล

อย่างที่ฉันบอกไปว่าฉันมีประสบการณ์จำกัดอย่างมากกับเวกเตอร์ เนื่องจากนี่เป็นครั้งแรกที่ฉันใช้มัน ฉันยังเป็นโปรแกรมเมอร์ที่ค่อนข้างใหม่ด้วย ดังนั้นการค้นคว้าวิจัยมากมายที่ฉันได้ทำเกี่ยวกับเรื่องนี้จึงเกินความคิดของฉันไป ความช่วยเหลือใด ๆ ที่สามารถให้ได้ในเรื่องนี้จะได้รับการชื่นชมอย่างมาก!


person Monique    schedule 10.06.2012    source แหล่งที่มา
comment
ฉันคิดว่าคุณสามารถใช้ boost::any ได้ อาร์กิวเมนต์เทมเพลตจะต้องส่งผ่านในเวลารวบรวม   -  person chris    schedule 10.06.2012
comment
องค์ประกอบทั้งหมดเป็นประเภทเดียวกันหรือผู้ใช้สามารถแทรกองค์ประกอบประเภทต่าง ๆ ในระหว่างการทำงานของโปรแกรมเดียวกันได้หรือไม่?   -  person juanchopanza    schedule 10.06.2012
comment
องค์ประกอบทั้งหมดต้องเป็นประเภทเดียวกันเมื่อผู้ใช้ป้อน   -  person Monique    schedule 10.06.2012
comment
และคุณจะทราบได้อย่างไรว่าประเภทใดขึ้นอยู่กับอินพุต   -  person juanchopanza    schedule 10.06.2012
comment
vector<void> ไม่ได้รับอนุญาต... vector<void*> คือ และจริงๆ แล้ว ข้อกำหนดทั้งหมดดูค่อนข้างแปลก คุณแน่ใจหรือไม่ว่านี่เป็นวิธีที่ดีที่สุดในการเข้าถึง และนี่คือการบ้านเหรอ?   -  person Zaid Amir    schedule 10.06.2012
comment
ใช่ นี่คือการบ้าน ฉันไม่แน่ใจว่านี่เป็นวิธีที่ดีที่สุดในการเข้าถึงไม่ใช่ ครูของฉันมักจะให้คำแนะนำในการมอบหมายงานที่กว้างมาก   -  person Monique    schedule 10.06.2012
comment
คำแนะนำของเราคือ: เขียนโปรแกรมด้วยภาษา C++ เพื่อทำการเรียงลำดับรายการองค์ประกอบ องค์ประกอบสามารถเป็นประเภทใดก็ได้ แต่ทั้งหมดจะเป็นประเภทเดียวกัน เช่น จำนวนเต็มทั้งหมด หรือทศนิยมทั้งหมด หรือตัวอักษรทั้งหมด หรือสตริงทั้งหมด (สตริงจะต้องเรียงลำดับเหมือนในพจนานุกรม) คุณสามารถใช้อัลกอริธึมการเรียงลำดับใดก็ได้ตามที่คุณต้องการ 1. ถามผู้ใช้ว่าจะมีองค์ประกอบกี่รายการ 2. ขอให้ผู้ใช้ป้อนองค์ประกอบ 3. ขอให้ผู้ใช้เลือกลำดับการเรียงลำดับ: จากน้อยไปมากหรือจากมากไปน้อยหรือทั้งสองอย่าง 4. พิมพ์ทั้งรายการอินพุตและเอาต์พุต 5. ผู้ใช้จะไม่ระบุใดๆ ข้อมูลเกี่ยวกับประเภทขององค์ประกอบ   -  person Monique    schedule 10.06.2012
comment
Yes, this is homework. จากนั้น คุณควรเพิ่มแท็กการบ้านในคำถามของคุณ   -  person Zaid Amir    schedule 10.06.2012
comment
ฉันขอโทษ ฉันไม่รู้ว่ามันสำคัญ... เลย ปัญหาของฉันไม่เพียงแค่ทำการบ้านให้เสร็จเท่านั้น แต่ยังเข้าใจแนวคิดเบื้องหลังสิ่งที่การบ้านของฉันใช้อีกด้วย   -  person Monique    schedule 10.06.2012
comment
@Monique: โดยทั่วไปแล้วสิ่งสำคัญคือเราจะตอบคำถามอย่างไร สำหรับการบ้าน คุณมีแนวโน้มที่จะได้รับคำแนะนำ (เพื่อที่คุณจะได้คิดออกเอง) และรายละเอียดเพิ่มเติมเกี่ยวกับแนวคิดที่ซ่อนอยู่ (เพราะเราไม่คิดว่าคุณรู้อะไรมากอยู่แล้ว)   -  person Matthieu M.    schedule 10.06.2012


คำตอบ (4)


C++ คือภาษาที่พิมพ์ แบบคงที่ หมายความว่าควรกำหนดทุกประเภทในระหว่างการคอมไพล์: คุณไม่สามารถแนะนำประเภทใหม่เมื่อรันโปรแกรม

  • การสร้างเวกเตอร์ประเภทโมฆะ (เห็นได้ชัดว่าไม่ได้รับอนุญาตตอนนี้เมื่อฉันค้นคว้าแล้ว โอ๊ะโอ)

void จริงๆ แล้วเป็นประเภทที่ค่อนข้างแปลก โดยส่วนใหญ่เป็นตัวยึดตำแหน่งสำหรับเวลาที่คุณคาดหวังประเภท (เช่น ประเภทการส่งคืนฟังก์ชัน) และไม่มีข้อมูลให้ void* ใช้เป็นตัวชี้ไปยังประเภทที่ไม่รู้จัก (ส่วนใหญ่เป็นภาษา C) แต่นี่ค่อนข้างเป็นการแฮ็ก เนื่องจากข้อมูลเกี่ยวกับต้นฉบับถูกละทิ้ง (เท่าที่เกี่ยวกับภาษา) ดังนั้นจึงทำให้เกิดปัญหาในการทำสิ่งต่าง ๆ ด้วยค่าจริง ได้รับดังนั้น

  • การโอเวอร์โหลดฟังก์ชันที่เรียกว่า insertInVector โดยการส่งองค์ประกอบแรกไปยังฟังก์ชัน และปล่อยให้ฟังก์ชันกำหนดประเภทเวกเตอร์ที่จะสร้างตามประเภทขององค์ประกอบแรก

  • #include <typeinfo> ในโปรแกรม ค้นหาประเภทขององค์ประกอบแรก จากนั้นสร้างเวกเตอร์โดยใช้ vector<typeid(firstElement).name()> และจริงๆ แล้วฉันไม่แน่ใจว่าทำไมมันถึงใช้งานไม่ได้ แต่มันก็ไม่ได้ผล

น่าเสียดายที่เป็นไปไม่ได้: เนื่องจากคุณไม่สามารถประกาศตัวแปรโดยไม่มีประเภทได้ firstElement จะขึ้นต้นด้วยประเภทใด


ปัญหาที่คุณกำลังอธิบายโดยทั่วไปนั้นยาก โดยพื้นฐานแล้วหมายความว่าคุณจะต้องยอมรับชุดอักขระ จากนั้นจึงเขียนโค้ดชุดกฎเพื่อกำหนดวิธีตีความอักขระเหล่านั้น ซึ่งทำได้โดยทั่วไปโดยใช้ ไวยากรณ์ เพื่อเข้ารหัสกฎเหล่านั้น แต่ไวยากรณ์อาจจะซับซ้อนสำหรับงานง่ายๆ

ผมขอยกตัวอย่างเล็กๆ น้อยๆ:

class Input {
public:
    enum Type {
        Int,
        Double,
        String
    };

    static Input Parse(std::string const& s);

    Input(): _type(Int), _int(0), _double(0.0) {} // need to define a default...

    Type type() const { return _type; }

    int asInt() const {
        assert(_type == Int && "not an int");
        return _int;
    }

    double asDouble() const {
        assert(_type == Double && "not a double");
        return _double;
    }

    std::string const& asString() const {
        assert(_type == String && "not a string");
        return _string; 
    }

private:
    Type _type;
    int _int;
    double _double;
    std::string _string;
};

แน่นอนว่าความท้าทายที่แท้จริงคือการ Parse อินพุตให้ถูกต้อง

แนวคิดคือการใช้ชุดกฎเกณฑ์ เช่น

  • int ประกอบด้วยตัวเลขเท่านั้น หรืออาจขึ้นต้นด้วย - ก็ได้
  • a double ประกอบด้วยตัวเลขเท่านั้น โดยมี . มากที่สุดหนึ่งตัว และเติมคำนำหน้าด้วย - หรือไม่ก็ได้
  • a string สามารถเป็นอะไรก็ได้ ดังนั้นจึงเป็นสิ่งที่จับได้ทั้งหมดของเรา

จากนั้นเราสามารถเขียนส่วนการจดจำของเมธอด Parse ได้:

static bool isInt(std::string const& s) {
    if (s.empty()) { return false; }
    
    // The first character may be among digits and '-'
    char const first = s.at(0);
    if (not isdigit(first) and first != '-') { return false; }

    // Subsequent characters may only be digits
    for (char c: s.substr(1)) {
        if (not isdigit(c)) { return false; }
    }

    // Looks like it is an int :)
    return true;
} // isInt

// Note: any int could be interpreted as a double too
static bool maybeDouble(std::string const& s) {
    if (s.empty()) { return false; }

    // The first character may be among digits, '.' and '-'
    char const first = s.at(0);
    if (not isdigit(first) and first != '.' and first != '-') { return false; }

    // There may only be one dot
    bool hasSeenDot = s.at(0) == '.';

    // Subsequent characters may only be digits and a dot now
    for (char c: s.substr(1)) {
        if (not isdigit(c) and c != '.') { return false; }

        if (c == '.') {
            if (hasSeenDot) { return false; } // no second dot allowed
            hasSeenDot = true;
        }
    }

    // Looks like it could be a double
    return true;
} // maybeDouble

static Input::Type guessType(std::string const& s) {
    if (isInt(s)) { return Input::Int; }

    // Test double after we ensured it was not an int
    if (maybeDouble(s)) { return Input::Double; }

    return Input::String;
} // guessType

และด้วยตรรกะการเดาร่วมกัน ในที่สุดการแยกวิเคราะห์ก็มาถึง:

Input Input::Parse(std::string const& s) {
    Input result;

    result._type = guessType(s);

    switch(result._type) {
    case Input::Int: {
        std::istringstream stream(s);
        s >> result._int;
        return result;
    }
    case Input::Double: {
        std::istringstream stream(s);
        s >> result._double;
        return result;
    }
    case Input::String:
        result._string = s;
        return result;
    }

    // Unreachable (normally)
    abort();
} // Input::Parse

วุ้ย

ดังนั้น ? เกือบจะมี. ตอนนี้เราจำเป็นต้องกำหนดวิธีเปรียบเทียบสองอินพุต เป็นเรื่องง่ายหากทั้งหมดมีประเภทเดียวกัน ถ้าไม่เช่นนั้น คุณจะต้องกำหนดตรรกะที่กำหนดเอง คุณสามารถแปลงอินพุต Int ในอินพุต Double ได้อย่างง่ายดายเพียงพอ แต่สำหรับสตริงมันจะแปลกกว่าเล็กน้อย

// define < for comparing two instance of "Input",
// assuming they both have the same type
bool operator<(Input const& left, Input const& right) {
    assert(left.type() == right.type() && "Different Types!");

    switch(left.type()) {
    case Input::Int: return left.asInt() < right.asInt();
    case Input::Double: return left.asDouble() < right.asDouble();
    case Input::String: return left.asString() < right.asString();
    }
} // operator<

และสุดท้ายคือโปรแกรม:

int main(int argc, char* argv[]) {
    // parse command line
    std::vector<Input> inputs;

    // by convention argv[0] is the program name, it does not count!
    for (int i = 1; i != argc; ++i) {
        inputs.push_back(Input::Parse(argv[i]));

        // Detect that the type is the same as the first input
        if (inputs.size() >= 2) {
            if (inputs.back().type() != inputs.front().type()) {
                std::cerr << "Please only use one type among Int, Double and String\n";
                return 1; // non-0 is an error
            }
        }
    }

    // sort
    std::sort(inputs.begin(), inputs.end());

    // echo back to the user
    for (Input const& i: inputs) {
        switch(i.type()) {
        case Input::Int: std::cout << i.asInt() << "\n"; break;
        case Input::Double: std::cout << i.asDouble() << "\n"; break;
        case Input::String: std::cout << i.asString() << "\n"; break;
        }
    }

    // End of the program
    return 0;
}

แน่นอนว่าฉันไม่รู้ว่าคุณต้องการจัดการกับประเภทไหน .. ฉันตัดสินใจเลือกชุดที่ต้องการแล้ว ;) อย่างไรก็ตาม สิ่งนี้น่าจะให้โครงกระดูกแก่คุณในการวางรากฐาน

person Matthieu M.    schedule 10.06.2012
comment
นั่นเป็นคำตอบที่ดี สิ่งเดียวที่ฉันไม่ชอบดีไซน์นี้ก็คือมันโลภนิดหน่อยในแง่ของความต้องการหน่วยความจำ แต่ฉันคิดว่ามันไม่สำคัญในกรณีของเธอ - person akappa; 10.06.2012
comment
นี่เป็นคำตอบที่ดี แต่เห็นได้ชัดว่าจุดประสงค์ของงานคือการใช้อัลกอริธึมการเรียงลำดับ ดังนั้นฉันจะลบคำแนะนำให้ใช้ std::sort เป็นการส่วนตัว อย่างไรก็ตาม +1 สำหรับรายละเอียด (และ ไม่ แก้ไขปัญหาของ OP) - person Fraser; 10.06.2012
comment
ไม่เคยได้ยินเกี่ยวกับ boost::variant เหรอ? นั่นจะเหมาะกว่าการเขียนของคุณเองมาก นอกจากนี้คุณลืมคำสั่ง break บนสวิตช์ - person Puppy; 13.06.2012
comment
ดูเหมือนว่าจะต้องใช้มากเกินไป เนื่องจากที่ เดียว ที่คุณต้องการประเภทเลยคือฟังก์ชันการเปรียบเทียบ - person Mooing Duck; 13.06.2012
comment
@DeadMG: ขอบคุณสำหรับ break สำหรับ boost::variant =› ไม่เหมาะกับระดับ OP ฉันกลัว; เรากำลังพูดถึงมือใหม่ที่ยังต้องดิ้นรนกับแนวคิดการพิมพ์แบบคงที่ - person Matthieu M.; 13.06.2012
comment
@Matthieu: ซึ่งเป็นเหตุผลว่าทำไมเขาถึงต้องเริ่มต้นกับสิ่งดีๆ ไม่ใช่การแฮ็กด้วยมือที่แย่มาก - person Puppy; 13.06.2012
comment
@DeadMG: ถ้าอย่างนั้นเราจะต้องตกลงที่จะไม่เห็นด้วย ฉัน คิดว่า boost::variant และแนวคิดของการเยี่ยมชมแบบคงที่นั้นซับซ้อนเกินไป (มากเกินไป) สำหรับงานที่ทำอยู่ - person Matthieu M.; 13.06.2012

เมื่อดูข้อกำหนดที่แท้จริงของปัญหาตามที่ระบุไว้ในความคิดเห็น ฉันขอแนะนำให้คุณเก็บอินพุตทั้งหมดใน std::vector<std::string> และจัดเรียงเวกเตอร์โดยใช้ std::sort ดังนั้น แทนที่จะกังวลเกี่ยวกับประเภทต่างๆ คุณสามารถระบุตรรกะการเรียงลำดับได้ โดยขึ้นอยู่กับสิ่งที่คุณตีความสตริงในเวกเตอร์ของคุณเพื่อนำเสนอ ดังนั้น

  1. ใช้ฟังก์ชันการเรียงลำดับสำหรับสตริงโดยขึ้นอยู่กับว่าสตริงเป็นตัวแทนอะไร (เพิ่มเติมในภายหลัง)
  2. เก็บอินพุตเป็นสตริงในเวกเตอร์
  3. กำหนดประเภทที่สตริงเป็นตัวแทน
  4. เลือกฟังก์ชันการเรียงลำดับตามประเภทนี้
  5. จัดเรียงเวกเตอร์โดยใช้ std::sort และฟังก์ชันการเรียงลำดับที่เหมาะสม

เกี่ยวกับฟังก์ชันการเรียงลำดับ std::sort ยอมรับไบนารีฟังก์ชันหรือฟังก์ชันที่ใช้การเปรียบเทียบ "น้อยกว่า" กับสององค์ประกอบ ดังนั้นฟังก์ชันหรือฟังก์ชันของคุณควรมีลักษณะดังนี้

bool foo(const std::string& rhs, const std::string& lhs) {
  // implement the logic
}

แก้ไข: เมื่อดูความคิดเห็นล่าสุด ดูเหมือนว่าจุดประสงค์หลักหากแบบฝึกหัดอาจเป็นการใช้อัลกอริธึมการเรียงลำดับสำหรับประเภทต่างๆ ในกรณีนั้น ฉันขอแนะนำให้ปฏิบัติตามแนวทางที่ไลบรารีมาตรฐาน C++ นำไปใช้ นั่นคือ เพื่อใช้การเรียงลำดับในแง่หรือการเปรียบเทียบ น้อยกว่า ระหว่างสองประเภท ดังนั้นจึงแยกตรรกะการเรียงลำดับออกจากประเภท ที่จะจัดเรียง ดังนั้น คุณจึงต้องการฟังก์ชันการเรียงลำดับเทมเพลต ซึ่งมีเทมเพลตตามประเภทตัววนซ้ำและฟังก์ชัน/ฟังก์ชันการเปรียบเทียบ

person juanchopanza    schedule 10.06.2012
comment
ลำดับพจนานุกรมแตกต่างจากลำดับตัวเลข - person akappa; 10.06.2012
comment
@akappa สามารถระบุตรรกะการเรียงลำดับได้ - person juanchopanza; 10.06.2012
comment
@juanchopanza คุณไม่คิดว่าคุณสามารถใช้อัลกอริธึมการเรียงลำดับใด ๆ ที่คุณเลือกโดยบอกเป็นนัยว่าพวกเขาต้องเขียนอัลกอริทึมของตัวเองแทนที่จะใช้ std::sort? - person Fraser; 10.06.2012
comment
@Fraser อาจจะ ฉันกำลังดิ้นรนเพื่อระบุจุดที่แท้จริงของการฝึก - person juanchopanza; 10.06.2012
comment
@Fraser: หรือสามารถระบุอาร์กิวเมนต์ที่ 3 เมื่อโทร std::sort - person Matthieu M.; 10.06.2012
comment
@juanchopanza ใช่ - ฉันค่อนข้างสงสัยว่าครูหมายถึงประเด็นของแบบฝึกหัดเพื่อใช้อัลกอริธึมการเรียงลำดับ แต่ทำให้ส่วนที่ยากที่สุดในการสร้างคอนเทนเนอร์โดยไม่ตั้งใจ - person Fraser; 10.06.2012
comment
@Fraser ใช่ การใช้ฟังก์ชันการเรียงลำดับเป็นหนึ่งในงาน CS แบบคลาสสิกเหล่านั้น - person juanchopanza; 10.06.2012
comment
@MatthieuM. นั่นถือเป็นการใช้อัลกอริธึมการเรียงลำดับหรือไม่? เดาว่ามันขึ้นอยู่กับการตีความของครู - person Fraser; 10.06.2012
comment
ใช่ คุณพูดถูก จุดประสงค์ของแบบฝึกหัดคือการใช้อัลกอริธึมการเรียงลำดับ -_- น่าเสียดายที่เขาไม่มีประสบการณ์ในการสอน (นักศึกษาระดับบัณฑิตศึกษา) และไม่ได้ลงลึกในเรื่องเวกเตอร์ เพราะจริงๆ แล้วเวกเตอร์เหล่านั้นไม่ควรครอบคลุมถึง จนกระทั่งบทต่อๆ ไป...พวกเขาเป็นเพียงการแนะนำสั้นๆ จนถึงตอนนี้ แต่ฉันไม่คิดว่าเขาจะเข้าใจเรื่องนี้ - person Monique; 10.06.2012
comment
@Monique ในกรณีนี้ ฉันขอแนะนำสิ่งนี้หรือคำตอบของ MatthieuM แต่แทนที่ std::sort ด้วยอัลกอริทึมการเรียงลำดับของคุณเอง - person Fraser; 10.06.2012
comment
@Monique: ดังนั้นเขาจึงผสมผสานงานอัลกอริทึมเข้ากับการออกแบบ (และค่อนข้างก้าวหน้าในเรื่องนั้น)? เขาจะต้องถูกห้ามไม่ให้สอน - person akappa; 10.06.2012
comment
@akappa: หรือบางทีอาจแค่แสดงวิธีที่ดีกว่าใคร ๆ ก็หวังว่าครูที่แท้จริงจะออกแบบหลักสูตรให้ครอบคลุม :/ - person Matthieu M.; 10.06.2012

หากคุณทราบว่าผู้ใช้สามารถป้อนข้อมูลประเภทใด คุณสามารถใช้เทมเพลตและการสืบทอดได้:

class Generic {
public:
  virtual void process_input() = 0; // Handles the next input from user
  virtual void process_output() = 0; // Processes the data inserted
};

template <typename T>
class HandleInput : public Generic {
private:
    std::vector<T> storage;
public:
    HandleInput(T first)
    {
      storage.push_back(first);
    }

    void process_input()
    {
      // do whatever you want
    }

    void process_output()
    {
      // do whatever you want
    }
};

int main(int argc, char **argv)
{
  // Get first input
  Input i = input();
  Generic *g;

  // Instantiate the "right" generic with a switch
  switch (i.type) {
    case T:
      g = new HandleInput<T>(i.value);
  }

  // Use Generic from here onwards
}

นั่นเป็นเพียงแนวคิด (Input ไม่สามารถนำไปใช้เช่นนั้นได้ คุณต้องเปลี่ยนส่วนนั้นด้วยตรรกะที่ได้รับบางอย่างจากผู้ใช้และกำหนดประเภทของมัน) แต่มันมีประโยชน์ในการมาสก์ประเภทนั้นเป็นคลาสทั่วไป ดังนั้นคุณจึงสามารถ แยกตัวประกอบโค้ดของคุณรอบๆ อินเทอร์เฟซที่จัดทำโดย Generic

แนวคิดอีกอย่างหนึ่ง (ง่ายกว่านั้นน่าจะ) คือการใช้ std::vector<void*> และ enum ที่บอกคุณว่าประเภทของข้อมูลที่จัดเก็บไว้ในเวกเตอร์คืออะไร เมื่อคุณจำเป็นต้องประมวลผลข้อมูลนั้นที่ใดที่หนึ่งในอนาคต คุณสามารถเปิด enum เพื่อแปลงองค์ประกอบเวกเตอร์ให้เป็นประเภทที่ถูกต้องและส่งไปยังโค้ดที่เหมาะสมได้

แก้ไข: อีกแนวคิดหนึ่งคือการกำหนดฟังก์ชันเทมเพลตที่รับอินพุตและเรียงลำดับอาร์เรย์โดยใช้ตัวเปรียบเทียบมาตรฐาน:

#include <iostream>

#include <vector>
#include <algorithm>
#include <boost/lexical_cast.hpp>

template <typename T>
void print_v(std::vector<T> &v)
{
    typename std::vector<T>::iterator it;
    for (it = v.begin(); it != v.end(); it++)
        std::cout << *it << " ";
    std::cout << std::endl;
}

template <typename T>
void sort_and_print(T first, size_t n, bool asc)
{
    std::vector<T> v;
    v.push_back(first);
    for (size_t i = 0; i < n; i++) {
        std::string s;
        std::cin >> s;
        T e = boost::lexical_cast<T>(s);
        v.push_back(e);
    }

    print_v(v);
    if (asc)
        std::sort(v.begin(), v.end(), std::greater<T>());
    else
        std::sort(v.begin(), v.end());
    print_v(v);
}

int main(int argc, char **argv)
{
    std::string s = "test";
    sort_and_print(s, 2, true);
    unsigned int j = 3;
    sort_and_print(j, 2, true);
    return 0;
}

ตรรกะในการกำหนดประเภทของอินพุตแรกนั้นขึ้นอยู่กับคุณ (บางทีคุณสามารถเปิดคำถามอื่นได้) ;)

person akappa    schedule 10.06.2012
comment
ฉันกลัวว่าคุณจะไปไกลจากสิ่งที่ OP เข้าใจมากเกินไป เขายังไม่ได้บูรณาการแนวคิดการพิมพ์แบบคงที่และคุณก็พร้อมแล้วในเทมเพลต (ไม่ได้บอกว่ากระแสตอบรับแย่มาก แค่กลัวเกินจะเข้าใจ) - person Matthieu M.; 10.06.2012
comment
@MatthieuM.: อาจจะ แต่ฉันไม่รู้ว่าเธอจะคิดสิ่งที่ง่ายกว่านี้ได้อย่างไรซึ่งไม่เกี่ยวข้องกับ boost หรืออะไรที่ซับซ้อนกว่านี้ (ยกเว้นการใช้เวกเตอร์ของ void* อาจจะ แต่ฉันไม่แน่ใจว่าจะสูญเสียประเภทนั้นไป) อาจนำไปสู่สิ่งที่มีประโยชน์) - person akappa; 10.06.2012
comment
@akappa ดูเหมือนว่ามันอาจจะได้ผล แต่ Matthieu พูดถูก มันอยู่เหนือหัวฉันนิดหน่อย ปัญหาหลักประการหนึ่งที่ฉันกำลังเผชิญอยู่ก็คือ ฉันไม่รู้วิธีระบุประเภทของอินพุตที่ฉันได้รับจากผู้ใช้ ฉันรู้วิธีใช้ฟังก์ชันเช่น isdigit เพื่อแยกความแตกต่างระหว่างอักขระและตัวเลข แต่นอกเหนือจากนั้น ฉันไม่รู้ =\ - person Monique; 10.06.2012
comment
@Monique: ความพยายามของคุณในการสรุปรายละเอียดนั้นน่ารักและเป็นแนวทางปฏิบัติที่ดี แต่ฉันสงสัยว่ารายละเอียดเกี่ยวกับประเภทที่คุณจะได้รับจากอินพุตและวิธีที่คุณควรแยกแยะความแตกต่างเหล่านั้นอาจมีประโยชน์จริง ๆ - person akappa; 10.06.2012
comment
แต่สวิตช์ทำงานอย่างไร? ดูเหมือนว่าคุณจะใช้ประเภทสำหรับเคส ซึ่งต้องใช้นิพจน์คงที่ - person juanchopanza; 10.06.2012
comment
ฉันเห็นด้วย ฉันไม่ได้พยายามสรุปรายละเอียด ฉันแค่ไม่แน่ใจว่าจะค้นหารายละเอียดได้อย่างไร ฉันไม่ได้รับอนุญาตให้ขอให้ผู้ใช้ป้อนข้อมูลประเภทต่างๆ และฉันก็ไม่สามารถแยกแยะความแตกต่างนอกเหนือจากนั้นได้ - person Monique; 10.06.2012
comment
@Monique: คุณรู้บางอย่างเกี่ยวกับเทมเพลตหรือไม่? - person akappa; 10.06.2012
comment
@juanchopanza: อ่านความคิดเห็นด้านล่างโค้ด มันเป็นเพียงรหัสเทียมที่แสดงแนวคิด - person akappa; 10.06.2012
comment
@akappa ไม่มาก ฉันลงเรือลำเดียวกันโดยใช้เทมเพลตเหมือนกับที่ใช้เวกเตอร์ ครูของฉันสอนแนวคิดพื้นฐานแต่ไม่มากไปกว่านั้น ฉันเป็นคนประเภทที่เรียนรู้ได้ดีจากตัวอย่างและทำงานกับสิ่งต่างๆ โดยตรง แต่ฉันไม่ได้เรียนรู้ง่ายๆ แค่อ่านหนังสือ ฯลฯ ชั้นเรียนนี้เหมือนฝันร้ายสำหรับฉัน - person Monique; 10.06.2012
comment
@Monique: โอเค โดยพื้นฐานแล้วมีสองวิธีที่นี่: (1) ไม่สนใจประเภทเมื่อแทรกในเวกเตอร์ แต่คำนึงถึงเมื่อคุณเรียงลำดับมัน (เช่นวิธีแก้ปัญหาของ juanchopanza) หรือ (2) ตรวจจับประเภท ในตอนแรกอินพุตและเขียนฟังก์ชันเทมเพลตด้วยประเภท T ที่ถูกต้อง ซึ่งคุณจะใช้ในคำจำกัดความของเวกเตอร์ (หลังจากนั้นคุณสามารถใช้ std::sort(v.begin(), v.end(), std::greater แบบธรรมดา / std::lesser) (2) ดีกว่าในทางทฤษฎี (คุณทำการแปลงข้อมูลเพียงครั้งเดียว แทนที่จะเป็น O(log n) ครั้งโดยเฉลี่ยสำหรับแต่ละองค์ประกอบ) แต่อาจจะซับซ้อนกว่าเล็กน้อย - person akappa; 10.06.2012
comment
ฉันอยากทำวิธีที่ 2 ดีกว่า เพราะครูบอกในชั้นเรียนว่าโปรแกรมของเราควรจะแยกแยะระหว่างประเภทต่างๆ ได้ แต่จะตรวจจับประเภทนั้นได้อย่างไรเมื่อป้อนข้อมูลครั้งแรก?? และมันเชื่อมโยงกับเทมเพลตได้อย่างไร? ดูเหมือนว่าถ้าฉันสามารถตรวจจับประเภทนั้นได้ตั้งแต่อินพุตแรก หลังจากอินพุตครั้งแรกนั้น ฉันสามารถสร้างเวกเตอร์ด้วยประเภทนั้นได้ =\ - person Monique; 10.06.2012
comment
@Monique: บิตการแยกวิเคราะห์อธิบายไว้ในคำตอบที่ยอดเยี่ยมของ Matthieu (คุณสามารถใช้ฟังก์ชัน guessType() ของเขาในการป้อนข้อมูลครั้งแรกจากนั้นเรียกใช้ฟังก์ชันเทมเพลตที่ถูกต้องพร้อมสวิตช์ที่ส่งคืนฟังก์ชัน) คุณสามารถดูได้ว่าเทมเพลตเกี่ยวข้องกับความแตกต่างของประเภทอย่างไรในโค้ดที่ฉันแนบมาในการอัปเดตคำตอบ - person akappa; 10.06.2012
comment
@akappa ใช่ การแก้ไขที่คุณทำนั้นเป็นสิ่งที่ฉันส่วนใหญ่เข้าใจ มันสมบูรณ์แบบ :D คุณรังเกียจไหมถ้าฉันถามคำถามสองสามข้อเกี่ยวกับเรื่องนี้ในการแชทกับคุณ? ฉันรู้สึกว่ามันอุดตันความคิดเห็นมากพอแล้ว - person Monique; 10.06.2012
comment
จริงๆ แล้ว nvm ฉันต้องไปทำงานแล้ว ›.‹ ขอบคุณมากสำหรับความช่วยเหลือของคุณทุกคน ฉันซาบซึ้งจริงๆ - person Monique; 10.06.2012

คำถามนี้มีสองประเด็น: การแยกวิเคราะห์และการเรียงลำดับ

  • คุณสามารถใช้ นิพจน์ทั่วไป เพื่อตรวจสอบประเภทข้อมูลที่ผู้ใช้ป้อน
  • คุณสามารถใช้ cin เพื่อแยกวิเคราะห์ข้อมูล

ขั้นแรก: ตระหนักว่าคุณไม่สามารถจำเป็นทราบประเภทของการป้อนข้อมูลของผู้ใช้ของคุณจนกว่าคุณจะได้รับทั้งหมด ~เช่น: พิจารณารายชื่อผู้ใช้ ชื่อ :

728278243
390349346
495045594
elizabeth

ดังนั้น ไม่ควรถือว่ารู้ดีที่สุดเกี่ยวกับข้อมูลขาเข้า (อาจนำไปสู่ประสบการณ์ผู้ใช้ที่น่าหงุดหงิด) แต่เลือกที่จะปฏิบัติต่อทุกอย่างเหมือนเป็นสตริงแทน จัดเก็บอินพุตดิบทั้งหมดเป็นสตริง เพื่อให้คุณสามารถเอาต์พุตในรูปแบบเดียวกับอินพุตได้ คุณสามารถใช้ say ซึ่งเป็นประเภทที่แจกแจงเพื่อสลับภายในตัวเปรียบเทียบการเรียงลำดับ หรือ พิจารณาใช้ mutliset/multimap ที่นี่ คุณจะสร้างชุด สั่งซื้อ ดังนั้นจึงไม่จำเป็นต้องเรียงลำดับ หมายเหตุ: ความซับซ้อนในการสร้างชุดลำดับขององค์ประกอบ N หรือ สำหรับการเรียงลำดับเดี่ยวบนองค์ประกอบรายการที่ไม่เรียงลำดับ N นั้นเทียบเท่าโดยประมาณ ~> NlogN

สำหรับงานในมือของคุณ มันแทบจะไม่สำคัญเลย แต่ในความเป็นจริงแล้ว ขึ้นอยู่กับวิธีการใช้รายการ แนวทางใดวิธีหนึ่งจะเหมาะสมกว่าในแง่ของประสิทธิภาพ

หากคุณได้ใช้การถูกใจของ std::vector แล้ว std::multimap ก็ไม่น่าจะน่ากลัวเกินไป โดยทั่วไปแล้ว มันเป็นอาร์เรย์ที่เกี่ยวข้องกันของคู่คีย์-ค่า multi ที่นี่หมายความว่ามันสามารถเก็บหลายองค์ประกอบด้วยคีย์ เดียวกัน (ซึ่งที่นี่คุณต้องการ)


ในตัวอย่างนี้ ฉันกำลังใช้ไลบรารี boost regex เพื่อกำหนดประเภทข้อมูลอินพุต funky บางส่วน
(เช่น: sudo apt-get install libboost-regex1.46-dev)

regex นี้อาจดูเป็นความลับ แต่มีตัวอย่างมากมายบน i/web สำหรับเกือบทุกรูปแบบที่เป็นไปได้ [หมายเหตุ: C++11 regex ค่อนข้างเป็นการแทนที่แบบดรอปอินสำหรับ boost regex เช่น: boost regex ควรเข้ากันได้กับมาตรฐาน C ++ 11 ที่เกิดขึ้นใหม่]


blah.cpp:

#include <iostream>
#include <sstream>
#include <string>
#include <list>
#include <map>
#include <set>
#include <boost/regex.hpp>    
//NB: GNU gcc added *experimental support for regular expressions in TR1 v 4.3.0.
//    compile with:  -std=c++0x

using namespace std;
using namespace boost;

//some example input data-types (perhaps notably missing a date!) 
const regex re_char("[^0-9]", regex_constants::extended); //non numeric chars
const regex re_digit("[[:digit:]]+", regex_constants::extended); //a string of only digits in range [0..9] ~ie: Z+
const regex re_xdigit("0[xX][[:xdigit:]]+", regex_constants::extended); //support hex iff starts with '0x' or '0X'
const regex re_float("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?", regex_constants::extended); //all kinds of numbers


int main(int argc, char** argv)
{    
    int i, countc=0;
    double d;
    string str;
    int element_count;    

    do
    {
        cout << "how many elements will there be? "; 
        if (cin >> element_count) break;
        cin.clear();
        cin >> str;
        cout << "\033[A\033[2K" << flush;
    }
    while(13);
    cin.ignore(128,'\n'); 

    multimap<double, string> list_num; 
    multimap<double, string> list_fp; 
    //NB: below, by way of example, construction using the 'greater<int>' comparison class achieves _descending_ order 
    multimap<int, string, greater<int> > list_int; 
    list<string> list_str; 

    for (int next=0; next < element_count; next++)
    {
        cout << "\033[A\033[2K" << flush;
        cout << "enter next element in list ["<< next+1 << "/" << element_count << "] : "; 
        getline (cin,str);

        if (regex_match(str, re_xdigit))
        {
            //see all about manipulators here:
            //http://www.cplusplus.com/reference/iostream/istream/operator%3E%3E/
            stringstream(str) >> hex >> i;            
            list_int.insert(pair<int, string>(i, str)); 
            list_num.insert(pair<double, string>(i, str)); 
        }
        else if (regex_match(str, re_digit))
        {
            stringstream(str) >> i;            
            list_int.insert(pair<int, string>(i, str));            
            list_num.insert(pair<double, string>(i, str)); 
        }
        else if (regex_match(str, re_float))
        {
            stringstream(str) >> d;    
            list_fp.insert(pair<double, string>(d, str));        
            list_num.insert(pair<double, string>(d, str)); 
        } 

        if (regex_match(str, re_char)) countc++;      
        list_str.push_back(str);
    }    

    cout << "\033[A\033[2K" << flush;

    cout << "input: unsorted list:" << endl;
    for (list<string>::iterator it=list_str.begin(); it!=list_str.end(); it++) 
        cout << *it << endl;

    if (list_int.size() == element_count)
    {
        cout << endl << "output: sorted list of Z+ types:" << endl;
        for (multimap<int, string>::iterator it=list_int.begin() ; it != list_int.end(); it++ )
            cout << (*it).second << endl;
    }
    else if (list_fp.size() == element_count)
    {
        cout << endl << "output: sorted list of fp types:" << endl;
        for (multimap<double, string>::iterator it=list_fp.begin() ; it != list_fp.end(); it++ )
            cout << (*it).second << endl;
    }
    else if (list_num.size() == element_count)
    {
        cout << endl << "output: sorted list of numeric types:" << endl;
        for (multimap<double, string>::iterator it=list_num.begin() ; it != list_num.end(); it++ )
            cout << (*it).second << endl;
    }
    else //output as sorted strings ~but in _descending_ order, using reverse iterator, by way of example
    {
        list_str.sort(); //but best to use list_str.sort(greater<string>()); with forward iterators
        cout << endl << "output: sorted list of " <<  (countc == element_count ? "non numeric char" : "string") << " types:" << endl;
        for (list<string>::reverse_iterator it=list_str.rbegin(); it!=list_str.rend(); ++it) 
            cout << *it << endl;        
    }   

    return 0;
}

ตัวอย่างถูกรวบรวมและรันบน Ubuntu สิ่งบรรทัดคำสั่ง:

$
$ lsb_release -d
Description:    Ubuntu 11.10

$ g++ --version
g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 

$ g++ --pedantic -oblah blah.cpp -lboost_regex
$ ./blah
input: unsorted list:
4.77
2.0e+2
-.3
11
0x10

output: sorted list of numeric types:
-.3
4.77
11
0x10
2.0e+2
$


หมายเหตุ: นี่คือโค้ดตัวอย่าง:

  • มีการเพิ่มประสิทธิภาพมากมายที่สามารถทำได้ที่นี่ เห็นได้ชัดว่าคุณไม่จำเป็นต้องต้องการคอนเทนเนอร์ stl มากเท่าที่ฉันใช้อยู่
  • ฉันไม่ได้จัดการกับทิศทางของการเรียงลำดับอย่างเคร่งครัด (แต่แสดงสองสามวิธีที่อาจบรรลุผลได้)
  • อาจเป็นการดีที่จะสรุปฟังก์ชันการทำงานเฉพาะประเภทในออบเจ็กต์ C ++ มีคลาสพื้นฐาน & คลาสที่ได้รับสำหรับแต่ละ ประเภท ที่คุณต้องการสนับสนุน ~แต่การบ้านนี้ใช่ไหม - ดังนั้นอาจจะไม่คุ้มค่าที่จะลงเรือมากเกินไป ;)
person violet313    schedule 13.06.2012