将未知类型的元素输入向量 [英] Inputting elements of unknown type into a vector

查看:61
本文介绍了将未知类型的元素输入向量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个程序,该程序从用户那里获取元素并对其进行排序.对于此程序,我必须使用向量,因为在用户输入之前未知元素列表的大小.我们的指示是:<​​/p>

I'm working on a program that takes elements from a user and sorts them. For this program, I have to use a vector as the size of the element list is unknown prior to user input. Our instructions were:

用C ++写一个程序来实现元素列表的排序.元素可以是任何类型,但与所有元素一样,都可以是同一类型整数或所有浮点数或所有字符或所有字符串(字符串应为像在字典中排序).您可以实现任何排序算法您选择的.

Write a program in C++ to implement sorting of a list of elements. Elements can be of any type but will all be of same type, like all integers or all floats or all chars or all strings (strings shall be sorted like in a dictionary). You can implement any sorting algorithm of your choice.

  1. 询问用户那里有多少个元素
  2. 要求用户输入元素
  3. 要求用户选择排序顺序:升序或降序或两者
  4. 同时打印输入和输出列表
  5. 用户将不会提供有关元素类型的任何信息

我对向量不是很熟悉(老师基本上是课堂上的略读主题),而我的书并没有为我提供有关该主题的大量信息.我遇到的问题是,直到用户开始输入后,我才知道元素列表的类型.到目前为止,我已经尝试过:

I'm not very familiar with vectors (teacher basically skimmed topic in class) and my book isn't giving me a whole lot of information on the subject. The problem I'm running into is that I don't know the type of the element list until the user begins input. So far, I have tried:

  • 创建一个无效类型向量(很抱歉,我已经研究了它,显然现在不允许)
  • 通过将第一个元素发送到函数并让该函数根据第一个元素的类型来确定要创建的向量类型,来重载名为 insertInVector 的函数(当我使用该函数时,这似乎是我的最佳选择想到了这一点,除了在函数终止后我需要访问vector之外,这样最终也就不能了)
  • 在程序中
  • #include< typeinfo> ,找到第一个元素的类型,然后使用 vector< typeid(firstElement).name()> 创建矢量,说实话我不是确定为什么那没有用,但是没用.
  • creating a void type vector (obviously not allowed now that I've researched it, oops)
  • overloading a function called insertInVector by sending the first element to the function and letting the function determine which vector type to create based on the type of the first element (which seemed like my best option when I thought of it, except I need access to the vector after the function terminates, so that ended up being a no go, too)
  • #include <typeinfo> in program, finding the type of the first element, and then creating a vector using vector<typeid(firstElement).name()> and honestly I'm not sure why that didn't work, but it didn't.

就像我说的那样,我对矢量的使用经验非常有限,因为这是我第一次使用它们.我还是一个相当新的程序员,所以我对此所做的很多研究都超出了我的脑海.对此将给予的任何帮助将不胜感激!

Like I said I have EXTREMELY limited experience with vectors as this is my first time using them. I'm also a fairly new programmer so a lot of the research I've done on this has gone over my head. Any help that could be given in this would be GREATLY appreciated!

推荐答案

C ++是静态类型的语言.这意味着应该在编译期间确定所有类型:运行程序时不能引入新类型.

C++ is a language statically typed. It means that all the types should be determined during compilation: you cannot introduce new types when running the program.

  • 创建一个无效类型向量(很抱歉,我已经研究了它,显然现在不允许)

void 实际上是一个非常奇怪的类型,主要是占位符,用于表示您期望某个类型(例如函数返回类型)并且什么都没有提供的情况. void * 用作指向未知类型的指针(主要在C语言中),但这确实是一个hack,因为有关原始信息的信息被丢弃(就语言而言),这导致了用获得的价值实际做事的问题.

void is actually quite a strange type, mostly a placeholder for when you would expect a type (like a function return type) and have none to provide. void* is used as a pointer to an unknown type (mostly in C) but this is quite a hack, because the information about the original is discarded (as far as the language is concerned) so this causes issues to actually do things with the value so obtained.

  • 通过将第一个元素发送到函数并让该函数根据第一个元素的类型来确定要创建的向量类型,来重载名为insertInVector的函数

  • overloading a function called insertInVector by sending the first element to the function and letting the function determine which vector type to create based on the type of the first element

#include< typeinfo> ,找到第一个元素的类型,然后使用 vector< typeid(firstElement).name()>创建向量; ,老实说,我不确定为什么这行不通,但是没有.

#include <typeinfo> in program, finding the type of the first element, and then creating a vector using vector<typeid(firstElement).name()> and honestly I'm not sure why that didn't work, but it didn't.

不幸的是,这两种方法都不可行:由于您无法在没有类型的情况下声明变量,因此以 firstElement 开头的类型是什么?

Unfortunately neither is possible: since you cannot declare a variable without a type, what would be the type of firstElement to begin with ?

您所描述的问题通常很难解决.基本上,这意味着您将必须接受一个字符串,然后编写一组规则来确定如何解释这些字符.通常使用语法对这些规则进行编码,以完成此操作.但是对于可能很简单的任务而言,语法可能会变得很复杂.

The problem you are describing is difficult in general. Basically it means that you will have to accept a string of characters, and then code a set of rules to determine how to interpret those characters. This is done generically by using a grammar to encode those rules; but grammars might way complicated for what is probably a simple task.

让我举一个小例子:

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 解析输入.

Obviously, the real challenge is to correctly Parse the input.

想法是使用一组规则,例如:

The idea is to use a set of rules, for example:

  • int 仅由数字组成,可以选择以-
  • 作为前缀
  • 一个 double 仅由数字组成,最多包含一个..并且可选地以-
  • 为前缀
  • string 可以是任何东西,因此是我们的全部
  • an int is composed exclusively of digits, optionally prefixed by -
  • a double is composed exclusively of digits, with at most one . and optionally prefixed by -
  • a string can be anything, therefore is our catch-all

然后我们可以编写 Parse 方法的识别部分:

Then we can write the recognition part of the Parse method:

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

结合猜测逻辑,最后进行解析:

And with the guessing logic together, finally the parse comes:

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

Ph!

那么?差不多好了.现在我们需要确定如何比较两个输入.如果它们都具有相同的类型,这很容易,否则,您将需要确定任意逻辑.您可以很容易地将输入Int转换为Double,但是对于字符串来说有点奇怪.

So ? Almost there. Now we need to determine how to compare two inputs. It's easy if they all have the same type, if not you will need to determine an arbitrary logic. You can transform an input Int in an input Double easily enough, but for string it's a bit weirder.

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

当然,因为我不知道您要处理的类型..我决定了一个任意集;)但是,这应该为您提供了一个基础以自己为基础.

Of course as I don't know the types you wish to deal with.. I've decided an arbitrary set ;) However this should give you a skeleton to base yourself on.

这篇关于将未知类型的元素输入向量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆