Identifying mathematical sets from input.

A set is a sequence of comma separated elements enclosed in square brackets.
An element for a set can be an alpha-numeral or another set. Furthermore, the elements can be of different types. White space is ignored.

Examples of a few sets are as follows:
{1,2,3}
{a,b,c}
{{1,2},{3}}
{{}}
{{1,2,3}}
{1,a,{2,b}}

My current objective is to have a program that takes some input and reports to output whether that input produces a set.

The grammar for a set is as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
Set:
    "{" Sequence "}"

Sequence:
    <Empty>
    List

List:
    Element
    Element "," List

Element:
    Alpha-numeral
    Set
*/


The code is as follows: (Scroll to the bottom and start with set())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
bool set(std::istream& in = std::cin);

bool element(std::istream& in = std::cin) {
//an element is a letter, number, or set
    char ch;
    in >> ch;

    if(isalpha(ch)) {
        return true;
    }
    if(ch == '.' || isdigit(ch)) {
    //ch is a number, go ahead and read the entire number
        in.unget();
        double val;
        in >> val;
        return true;
    }
    //not a number or letter, hopefully this element is a set

    in.unget();
    return set(in);
}

bool list(std::istream& in = std::cin) {
//a list is an element or an element "," list
    if(!element(in)) {
        return false;
    }

    char ch;
    in >> ch;
    if(ch == ',') { //does the list continue?
        return list(in);
    }

    //no comma, either the only or last element in the list
    in.unget();
    return true;
}

bool sequence(std::istream& in = std::cin) {
//a sequence is either the empty set or a list
    char ch;
    in >> ch;

    if(ch == '}') {
    //empty set found
        in.unget();
        return true;
    }
    in.unget();
    return list(in);
}

bool set(std::istream& in) {
//a set is formatted as "{" Sequence "}"
    char ch;
    in >> ch;

    //every set must begin with a "{"
    if(ch == '{' && sequence(in)) {
        in >> ch;
        return ch == '}';
    }
    return false;
}

My current dilemma is that input such as...
{{}}}
{{}}}}}
{1,2,3}a
is considered a set. That is, even if there are characters trailing a proper set the program still recognizes the entire input as a set.

Any ideas on how this can be fixed?
Last edited on
An element for a set can be an alpha-numeral or another set ... objective is to have a program that takes some input

Since the program determines how input is read into it you can set up a std::deque<std::deque<std::string>> and read into it as shown in the following program, using comma-separated braces as set element delimiters. If you want to proceed with using this program I'd suggest:

(a) breaking up the code into smaller functions and de-cluttering main(),
(b) as errors might happen during entering set elements adding a pop_back to the mySet container as part of the menu
(c) input validation at std::cin level
(d) if you further want to use the data particularly the numeric data then consider converting them into int/double etc from std::string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <iostream>
#include <string>
#include <deque>
#include <limits>

int main()
{
    std::deque<std::deque<std::string>> mySet;
    std::deque<std::string> myElement {};
    bool fQuit = false;
    while(!fQuit)
    {
        std::cout << "1. Enter set element \t2. Quit \n";
        int choice;
        std::cin >> choice;
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        switch(choice)
        {
            case 1:
            {
                bool fQuit = false;
                while (!fQuit)
                {
                    std::cout << "a. Enter sub element \tb. Element complete, quit \n";
                    char choice;
                    std::cin >> choice;
                    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                    switch (choice)
                    {
                        case 'a':
                        {
                            std::string temp{};
                            std::cout << "Set sub element argument \n";
                            std::cin >> temp;
                            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
                            myElement.push_back(temp);
                        }
                        break;
                        case 'b':
                            mySet.push_back(myElement);
                            myElement.clear();
                            fQuit = true;
                            break;
                        default:
                            std::cout << "Wrong choice, try again \n";
                            break;
                    }
                }
            }
            break;
            case 2:
                fQuit = true;
                break;
                default:
                std::cout << "Wrong choice, try again \n";
                break;
        }
    }
    std::cout << "{";
    for (auto itr_outer = mySet.begin(); itr_outer != mySet.end(); ++itr_outer)
    {
        std::cout << "{";
        for (auto itr_inner = (*itr_outer).begin(); itr_inner != (*itr_outer).end(); ++itr_inner )
        {
            if(((*itr_outer).size()) == 1 || (&(*itr_inner) == &(*itr_outer)[(*itr_outer).size()-1]))
            {
                std::cout << *itr_inner;
            }
            else
            {
                std::cout << *itr_inner << ",";
            }
        }
        if((mySet.size() == 1) || &(*itr_outer) == &mySet[mySet.size() -1])
        {
            std::cout << "}";
        }
        else
        {
            std::cout << "},";
        }
    }
    std::cout << "}";
}

Sample Output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
1. Enter set element    2. Quit
1
a. Enter sub element    b. Element complete, quit
a
Set sub element argument
1
a. Enter sub element    b. Element complete, quit
b
1. Enter set element    2. Quit
1
a. Enter sub element    b. Element complete, quit
a
Set sub element argument
2
a. Enter sub element    b. Element complete, quit
a
Set sub element argument
b
a. Enter sub element    b. Element complete, quit
b
1. Enter set element    2. Quit
2
{{1},{2,b}}
Process returned 0 (0x0)   execution time : 27.748 s
Press any key to continue.
Last edited on
{{}}}}}
the bold part is interpreted correctly as a set, the rest is ignored.

So in your main() you should have something like if (set(std::cin) and reached_end_of_line(std::cin) )

by the way, perhaps you should change the name of your functions: is_set(), is_element()
OP: you could also have a struct Element for each element of the set with a ctor overload that'd query the size of each element and read data directly into the corresponding std::vector<std::string>.
Here's an outline:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <vector>
#include <string>
#include <utility>

struct Element
{
    unsigned int m_sizeOf;
    std::vector<std::string> m_subs;

    Element(const unsigned int& sizeOf) : m_sizeOf (sizeOf)
    {
        unsigned int tmp{};
        while (tmp < m_sizeOf)
        {
            std::cout << "Enter sub-element " << tmp + 1 << " of " << m_sizeOf << "\n";
            std::string tmp_s{};
            std::cin >> tmp_s;
            m_subs.push_back(std::move(tmp_s));
            tmp_s = " ";
            tmp++;
        }
    }
};

Last edited on
Topic archived. No new replies allowed.