finding (whole word) substrings in string

Pages: 12
Hey guys.
It's me again...

I'm trying to make a program that searches the inputed text for certain keywords to try and figure out what the user responded (Yes or No).

Example of user input: "naa, I'd rather not".
My algorithm searches the string sentence for keywords that help it figure out that the answer is no. In this case both "naa" and "i'd rather not" are in the no dictionary letting me know that the user is saying no.

I'm using a function that searches for keywords that are a substring in the inputed string.

1
2
3
4
5
  if (userInput.find(*it) != std::string::npos) 
		{
			checkedResult = 1;
			break;
		}

*it is the iterator that goes through the vector with the keywords

I'd like to tweak the function so that it only finds substrings that are whole words in a sentence.
Example: It will not find "no" in "why not" despite "no" being a part of the word "not" since the word "no" does not appear as a word on it's own.

Does anyone know how to make these adjustments?

More info:
-I'm using std::string , not char*
-I'd like to solve this only with libraries that are included with standard Visual Studios installation.

Thanks!
Last edited on
if you have a string full of words, you can search for spaces as delimiters and split it into substrings.
for a dissertation on 50 ways to do that:
https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c

Personally, some things are just easier if you KISS it. The whole mess can be solved with no fuss in C, but takes a bit of juggling to do in c++. I get why people don't want to go there, but they threw the baby out with the bath in a few key places.

Maybe something like:
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


#include <iostream>
#include <sstream>


int instr(std::string istring,std::string tofind){
    if (istring.find(tofind)!=istring.npos){
        return istring.find(tofind)+1;
    }else{
        return 0;
    }
}


int main()
{
	 std::istringstream dict( "no nope nae naa noway never" );
	 std::string s="yes,nope,OK,maybe,sure,anytime,yep,yea,naa,affirmative";
	 std::string v;
	 int i;
	 while ( dict >> v )
	 {
	 	i=instr(s,v);
	 	if (i) {std::cout<<v<<"  position = "<<i<<std::endl;};
     }
     std::cout<<std::endl;
     std::cout<<"Press enter to end . . ."<<std::endl;
std::cin.get();
	 
}  
I take it back :)
A little tampering with one of the ones from the link and it was equally as fast as strtok routine. It was only slow as written, due to not having reserve and other tweaks. The original was more flexible, but flexibility costs speed in this case. Its still longer than C would need, but code size isnt a reason to be a barbarian if nothing else is gained.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vector<string> split(const string& str) 
{
    vector<string> rv;    
	rv.reserve(20);
    size_t prev = 0, pos = 0;
    do
    {
        pos = str.find(' ', prev);
        if (pos == string::npos) 
		  pos = str.length();
        const string &token = str.substr(prev, pos-prev);
        if (!token.empty()) 
		  rv.push_back(token);
        prev = pos + 1;
    }
    while (pos < str.length()); 
    return rv;
}



the microscopically faster C mess for fun only, its of no real value:
I changed strtok for a hand rolled same thing loop to merge it with memcpy (also removed) ... basically tokenize and copy into cs all in one.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
vector<string> csplit(const string &s)
{
	static char cs[1000]; 		
	cs[s.length()] = 0;
	for(int i = 0; i < s.length(); i++)
	  cs[i] = (s[i]!=' ')*s[i];
	  
	vector<string> rv;    
	rv.reserve(20);	
	static char*cp;	
	int i = 0;
	while( i < s.length())
	{
	 cp = &cs[i];
	 rv.push_back(cp);
	 i += rv.back().length()+1;	 
	}
	
	return rv;
}


however, the full cheat full barbarian version is a full order of magnitude faster. It also isnt thread safe, or totally safe in general. I don't know any way to do this kind of speedup in c++. I am not sure its a very fair comparison, but it IS a lot faster. Its not fair because a real program probably needs to copy the data to preserve it anyway -- which is what this is avoiding. If you don't mind burning the data after, ... maybe. Its still ugly.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
vector<char*> csplit(const string &s)
{
	static char cs[1000]; 		
	cs[s.length()] = 0;
	for(int i = 0; i < s.length(); i++)
	  cs[i] = (s[i]!=' ')*s[i];
	  
	vector<char*> rv;    
	rv.reserve(20);	
	static char*cp;	
	int i = 0;
	while( i < s.length())
	{
	 cp = &cs[i];	 
	 i += strlen(cp)+1;	 	
	 rv.push_back(cp);	 
	}
	
	return rv;
}

Last edited on

jonnin/yem
Excuse the interjection yem.
I have recently joined this forum but I am unable to post a new topic, although I can reply, as here.
All the format buttons <> (code) and suchlike are dead, I get no preview.
I cannot raise admin, this is my only method of contact (reply button).
What could be the problem?

the forum is ancient and the admins are long gone.
All I can think of is try another browser. I don't know if it works well on phones or such.
dodicat, you can still make a new topic. You just can't preview it.
Manually type [code] and [/code], [output] [/output] [quote] [/quote] etc.

You can edit your post afterwards and the buttons and preview will work.

Thanks Ganado, I've got one in.
Thanks jonnin.
I tried Google, iexplore, and Microsoft edge for this.
I'm trying to make a program that searches the inputed text for certain keywords to try and figure out what the user responded (Yes or No).


Consider:

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
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <unordered_map>

enum class REPLY {Yes = 0, No, Unknown};

REPLY GetRes(const std::string& str)
{
	static const std::unordered_map<std::string, REPLY> dict {{"no", REPLY::No},  {"nope", REPLY::No},  {"nae", REPLY::No}, {"naa", REPLY::No},
		{"yes", REPLY::Yes}, {"OK", REPLY::Yes}, {"yea", REPLY::Yes}, {"sure", REPLY::Yes}, {"yep", REPLY::Yes}};

	std::istringstream iss(str);

	for (std::string w; iss >> w; )
		if (const auto isfnd {std::find_if(std::begin(dict), std::end(dict), [&w](auto rep) {return rep.first == w; })}; isfnd != std::end(dict))
			return isfnd->second;

	return REPLY::Unknown;
}

int main()
{
	std::string reply;

	std::cout << "Enter reply: ";
	std::getline(std::cin, reply);

	switch (GetRes(reply)) {
		case REPLY::Yes:
			std::cout << "Yes\n";
			break;

		case REPLY::No:
			std::cout << "No\n";
			break;

		case REPLY::Unknown:
			std::cout << "Unknown\n";
			break;
	}
}


The issue, of course, is what is the result if the input is say:


OK nope

??
Last edited on
seeplus

>>The issue, of course, is what is the result if the input is say: OK nope

I got that part covered. I have a vector to respond to unclear inputs with an output such as "Huh? I'm not sure I understand..." among 20 other replies that I have in a premade vector :)

I'm going to need to go over all of your codes. I didn't manage to understand it all.

Here's my response cpp file if you're interested in what I have so far (or have comments on bad programming)

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//responseVector cpp file

#include"responseVectors.h"

enum userResponse { error0, yes, no, error3 }; //in my program this line is in the header

vector<string> yesVector()
{
	vector<string> resVec;

	resVec.push_back("yes");
	resVec.push_back("ya");
	resVec.push_back("yeah");
	resVec.push_back("yea");
	resVec.push_back("yep");
	resVec.push_back("yup");
	resVec.push_back("ok");
	resVec.push_back("okay");
	resVec.push_back("sure");
	resVec.push_back("why not");
	resVec.push_back("fine");

	return resVec;
}

vector<string> noVector()
{
	vector<string> resVec;

	resVec.push_back("no");
	resVec.push_back("nope");
	resVec.push_back("na");
	resVec.push_back("naa");
	resVec.push_back("i'm good");
	resVec.push_back("not really");
	resVec.push_back("i'm ok");
	resVec.push_back("i'm okay");
	resVec.push_back("i'd rather not");
	resVec.push_back("i would rather not");

	return resVec;
}

vector<string> exceptionVector()
{
	vector<string> resVec;

	resVec.push_back("Huh? I'm not sure I understand...");
	resVec.push_back("Lol. I have no idea what you mean");
	resVec.push_back("say whaaaaa?");
	resVec.push_back("Um, I dunno what that means");
	resVec.push_back("Can you repeat that?");
	resVec.push_back("I honestly have no idea how to respond to that");
	resVec.push_back("you're cracking me up. I don't know what I'm supposed to say to that");
	resVec.push_back("come on, seriously");
	resVec.push_back("lol. not sure i understood you...");
	resVec.push_back("lol. I can also say stuff that doesn't make any sense!");
	resVec.push_back("I'm not stupid. I just have no idea what you're trying to tell me");
	resVec.push_back("come again?");
	resVec.push_back("Umm...\nthat makes just as much sense to me as biology does...");
	resVec.push_back("Is that code for something?");
	resVec.push_back("I have a feeling that you might be messing with me");
	resVec.push_back("Umm, what???");

	return resVec;
}

userResponse checkUserResponse(string userInput)
{
	std::transform(userInput.begin(), userInput.end(), userInput.begin(), ::tolower);

	int checkedResult = 0;

	vector<string> resVec;

	resVec = yesVector();

	vector<string>::iterator it;

	for (it = resVec.begin(); it != resVec.end(); it++)
	{
		if (userInput.find(*it) != std::string::npos) //if it is a subsentence of the user input
		{
			checkedResult = 1;
			break;
		}
	}
	
	resVec = noVector();

	for (it = resVec.begin(); it != resVec.end(); it++)
	{
		if (userInput.find(*it) != std::string::npos) //if it is a subsentence of the user input
		{
			checkedResult += 2;
			break;
		}
	}

	return (userResponse)checkedResult;
}
Last edited on
I combined the yes/no vectors into a single map specifying the string and its enum type rather than a vector of struct or two separate vectors.

Now that I've seen your code, dealing with input that is contradictory can be handled, as well as providing exception strings. Based upon using different vectors etc, consider:

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <algorithm>
#include <cctype>

auto Rand {std::mt19937 {std::random_device {}()}};

enum userResponse:unsigned { error0 = 0, yes, no, error3 };

std::string getExp()
{
	static const std::vector<std::string> resVec {
		"Huh? I'm not sure I understand...",
		"Lol. I have no idea what you mean",
		"say whaaaaa?",
		"Um, I dunno what that means",
		"Can you repeat that?",
		"I honestly have no idea how to respond to that",
		"you're cracking me up. I don't know what I'm supposed to say to that",
		"come on, seriously",
		"lol. not sure i understood you...",
		"lol. I can also say stuff that doesn't make any sense!",
		"I'm not stupid. I just have no idea what you're trying to tell me",
		"come again?",
		"Umm...\nthat makes just as much sense to me as biology does...",
		"Is that code for something?",
		"I have a feeling that you might be messing with me",
		"Umm, what???"
	};

	static auto randNo {std::uniform_int_distribution<size_t> {0, resVec.size() - 1}};

	return resVec[randNo(Rand)];
}

userResponse checkUserResponse(std::string userInput)
{
	static const std::vector<std::string> yesVec {
		"yes",
		"ya",
		"yeah",
		"yea",
		"yep",
		"yup",
		"ok",
		"okay",
		"sure",
		"why not",
		"fine"
	};

	static const std::vector<std::string> noVec {
		"no",
		"nope",
		"na",
		"naa",
		"i'm good",
		"not really",
		"i'm ok",
		"i'm okay",
		"i'd rather not",
		"i would rather not"
	};

	std::transform(userInput.begin(), userInput.end(), userInput.begin(), [](auto ch) {return (char)std::tolower(ch); });

	unsigned checkedResult {};

	for (const auto& yv : yesVec)
		if (userInput.find(yv) != std::string::npos) {
			checkedResult |= yes;
			break;
		}

	for (const auto& nv : noVec)
		if (userInput.find(nv) != std::string::npos) {
			checkedResult |= no;
			break;
		}

	return (userResponse)checkedResult;
}

int main()
{
	std::string reply;

	std::cout << "Enter reply: ";
	std::getline(std::cin, reply);

	switch (checkUserResponse(reply)) {
		case yes:
			std::cout << "Yes\n";
			break;

		case no:
			std::cout << "No\n";
			break;

		default:
			std::cout << getExp() << '\n';
			break;
	}
}



jonnin

I based my code off of your algorithm (which I liked) but found a different issue.
now that I break it up into single words, multi word keys (such as "i'm good") are no longer picked up.

Do you know how to solve that?

(I was thinking maybe it can be solve with Tries? not sure how exactly i would do that though)

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//responseVector cpp file

#include"responseVectors.h"

enum userResponse { error0, yes, no, error3 }; //in my program this line is in the header

vector<string> yesVector()
{
	vector<string> resVec;

	resVec.push_back("yes");
	resVec.push_back("ya");
	resVec.push_back("yeah");
	resVec.push_back("yea");
	resVec.push_back("yep");
	resVec.push_back("yup");
	resVec.push_back("ok");
	resVec.push_back("okay");
	resVec.push_back("sure");
	resVec.push_back("why not");
	resVec.push_back("fine");
	resVec.push_back("i guess");

	return resVec;
}

vector<string> noVector()
{
	vector<string> resVec;

	resVec.push_back("no");
	resVec.push_back("nope");
	resVec.push_back("na");
	resVec.push_back("naa");
	resVec.push_back("i'm good");
	resVec.push_back("not really");
	resVec.push_back("i'm ok");
	resVec.push_back("i'm okay");
	resVec.push_back("i'd rather not");
	resVec.push_back("i would rather not");

	return resVec;
}

vector<string> exceptionVector()
{
	vector<string> resVec;

	resVec.push_back("Huh? I'm not sure I understand...");
	resVec.push_back("Lol. I have no idea what you mean");
	resVec.push_back("say whaaaaa?");
	resVec.push_back("Um, I dunno what that means");
	resVec.push_back("Can you repeat that?");
	resVec.push_back("I honestly have no idea how to respond to that");
	resVec.push_back("you're cracking me up. I don't know what I'm supposed to say to that");
	resVec.push_back("come on, seriously");
	resVec.push_back("lol. not sure i understood you...");
	resVec.push_back("lol. I can also say stuff that doesn't make any sense!");
	resVec.push_back("I'm not stupid. I just have no idea what you're trying to tell me");
	resVec.push_back("come again?");
	resVec.push_back("Umm...\nthat makes just as much sense to me as biology does...");
	resVec.push_back("Is that code for something?");
	resVec.push_back("I have a feeling that you might be messing with me");
	resVec.push_back("Umm, what???");

	return resVec;

}

userResponse checkUserResponse(string userInput)
{
	std::transform(userInput.begin(), userInput.end(), userInput.begin(), ::tolower);

	int checkedResult = 0;

	vector<string> splitUserInput;

	splitUserInput = splitSentence(userInput);

	vector<string> resVec;

	resVec = yesVector();

	vector<string>::iterator it, jt;

	for (it = resVec.begin(); it != resVec.end(); it++)
	{
		for (jt = splitUserInput.begin(); jt != splitUserInput.end(); jt++)
		{
			if (*it == *jt) //if it is a subsentence of the user input
			{
				checkedResult = 1;
				break;
			}
		}
	}
	
	resVec = noVector();

	for (it = resVec.begin(); it != resVec.end(); it++)
	{
		for (jt = splitUserInput.begin(); jt != splitUserInput.end(); jt++)
		{
			if (*it == *jt) //if it is a subsentence of the user input
			{
				checkedResult += 2;
				return (userResponse)checkedResult;
			}
		}
	}

	return (userResponse)checkedResult;
}

vector<string> splitSentence(const string& str)
{
	vector<string> rv;
	rv.reserve(20);
	size_t prev = 0, pos = 0, pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0, pos5 = 0, pos6 = 0;
	do
	{
		pos1 = str.find(' ', prev);
		pos2= str.find(',', prev);
		pos3 = str.find('?', prev);
		pos4 = str.find('!', prev);
		pos5 = str.find('-', prev);
		pos6 = str.find('.', prev);

		pos = min(pos1, pos2);
		pos = min(pos, pos3);
		pos = min(pos, pos4);
		pos = min(pos, pos5);
		pos = min(pos, pos6);

		if (pos == string::npos)
			pos = str.length();
		const string &token = str.substr(prev, pos - prev);
		if (!token.empty())
			rv.push_back(token);
		prev = pos + 1;
	} while (pos < str.length());
	return rv;
}

int min(int x, int y)
{
	if (x <= y)
		return x;

	return y;
}
Last edited on
seeplus

Hey, I just saw your code now.

there was a few keywords/syntax that I wasn't able to understand...

1
2
3
4
5
6
for (const auto& yv : yesVec)
		if (userInput.find(yv) != std::string::npos) 
                {
			checkedResult |= yes;
			break;
		}

- what is the const auto keyword?
- what is this symbol? |=

also, does this algorithm solve the second issue I had (now that I break it up into single words, multi word keys (such as "i'm good") are no longer picked up)?

thanks...
Last edited on
const just means the variable can't be changed.
auto means that the type is automatically deduced and assigned to the variable from the type of the initializer (very useful in all sorts of places!)

: within a for loop is called the range-for. The variable defined before the : will sequentially begiven all the values from the container specified after the :

|= | is bit or (as opposed to || which is logical or). So checkedResult |= yes means checkedResult = checkedResult | yes

The issue is in deciding whether a given text is ambiguous or not.

Given an input of 'why not' then this meets the yes list ('why not') but also matches the no list ('no') and so is reported as ambiguous (error3). what is needed is to match whole words.

So consider this (not thoroughly tested):

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <algorithm>
#include <cctype>

auto Rand {std::mt19937 {std::random_device {}()}};

enum userResponse:unsigned { error0 = 0, yes, no, error3 };

std::string getExp()
{
	static const std::vector<std::string> resVec {
		"Huh? I'm not sure I understand...",
		"Lol. I have no idea what you mean",
		"say whaaaaa?",
		"Um, I dunno what that means",
		"Can you repeat that?",
		"I honestly have no idea how to respond to that",
		"you're cracking me up. I don't know what I'm supposed to say to that",
		"come on, seriously",
		"lol. not sure i understood you...",
		"lol. I can also say stuff that doesn't make any sense!",
		"I'm not stupid. I just have no idea what you're trying to tell me",
		"come again?",
		"Umm...\nthat makes just as much sense to me as biology does...",
		"Is that code for something?",
		"I have a feeling that you might be messing with me",
		"Umm, what???"
	};

	static auto randNo {std::uniform_int_distribution<size_t> {0, resVec.size() - 1}};

	return resVec[randNo(Rand)];
}

userResponse checkUserResponse(std::string userInput)
{
	static const std::vector<std::string> yesVec {
		"yes",
		"ya",
		"yeah",
		"yea",
		"yep",
		"yup",
		"ok",
		"okay",
		"sure",
		"why not",
		"fine"
	};

	static const std::vector<std::string> noVec {
		"no",
		"nope",
		"na",
		"naa",
		"i'm good",
		"not really",
		"i'm ok",
		"i'm okay",
		"i'd rather not",
		"i would rather not"
	};

	std::transform(userInput.begin(), userInput.end(), userInput.begin(), [](auto ch) {return (char)std::tolower(ch); });

	unsigned checkedResult {};

	for (const auto& yv : yesVec)
		for (auto f = 0; (f = userInput.find(yv, f)) != std::string::npos; ++f)
			if (f + yv.size() >= userInput.size() || std::isspace(userInput[f + std::size(yv)])) {
				checkedResult |= yes;
				break;
			}

	for (const auto& nv : noVec)
		for (auto f = 0; (f = userInput.find(nv, f)) != std::string::npos; ++f)
			if (f + nv.size() >= userInput.size() || std::isspace(userInput[f + std::size(nv)])) {
				checkedResult |= no;
				break;
			}

	return (userResponse)checkedResult;
}

int main()
{
	std::string reply;

	std::cout << "Enter reply: ";
	std::getline(std::cin, reply);

	switch (checkUserResponse(reply)) {
		case yes:
			std::cout << "Yes\n";
			break;

		case no:
			std::cout << "No\n";
			break;

		default:
			std::cout << getExp() << '\n';
			break;
	}
}


The issue with this is an input of say 'i'm ok'

ok is parsed as being yes and then "i'm ok" is parsed as being no - so returns error3 as could be either.

I've got an idea I'll try....
Last edited on
seeplus

I quickly tried compiling your program but there seems to be a syntax issue on lines 72,79 regarding the semicolen in the middle of the if statement.
 
if (const auto f = userInput.find(yv); f != std::string::npos)

Am I missing something here?

Also I'm having trouble understanding the purpose of the {} in line 69
 
unsigned checkedResult {};


Also, it appears to me that for input "i'm ok" it will not get a no as a response, instead it will get an ambiguous response since despite "i'm ok" being in the noVector, "ok" appears in the yes vector. Am I mistaken?
Last edited on
Try this:

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <algorithm>
#include <cctype>
#include <map>

auto Rand {std::mt19937 {std::random_device {}()}};

enum userResponse:unsigned { error0 = 0, yes, no, error3 };

std::string getExp()
{
	static const std::vector<std::string> resVec {
		"Huh? I'm not sure I understand...",
		"Lol. I have no idea what you mean",
		"say whaaaaa?",
		"Um, I dunno what that means",
		"Can you repeat that?",
		"I honestly have no idea how to respond to that",
		"you're cracking me up. I don't know what I'm supposed to say to that",
		"come on, seriously",
		"lol. not sure i understood you...",
		"lol. I can also say stuff that doesn't make any sense!",
		"I'm not stupid. I just have no idea what you're trying to tell me",
		"come again?",
		"Umm...\nthat makes just as much sense to me as biology does...",
		"Is that code for something?",
		"I have a feeling that you might be messing with me",
		"Umm, what???"
	};

	static auto randNo {std::uniform_int_distribution<size_t> {0, resVec.size() - 1}};

	return resVec[randNo(Rand)];
}

userResponse checkUserResponse(std::string userInput)
{
	struct classcomp {
		bool operator() (const std::string& lhs, const std::string& rhs) const
		{
			return lhs.size() > rhs.size();
		}
	};

	static const std::multimap<std::string, userResponse, classcomp> res {
		{"yes", yes},
		{"ya", yes},
		{"yeah", yes},
		{"yea", yes},
		{"yep", yes},
		{"yup", yes},
		{"ok", yes},
		{"okay", yes},
		{"sure", yes},
		{"why not", yes},
		{"fine", yes},
		{"no", no},
		{"nope", no},
		{"na", no},
		{"naa", no},
		{"i'm good", no},
		{"not really", no},
		{"i'm ok", no},
		{"i'm okay", no},
		{"i'd rather not", no},
		{"i would rather not", no}
	};

	std::transform(userInput.begin(), userInput.end(), userInput.begin(), [](auto ch) {return (char)std::tolower(ch); });

	unsigned checkedResult {error0};

	for (const auto& [word, typ] : res)
		for (size_t f = 0; (f = userInput.find(word, f)) != std::string::npos; ++f)
			if ((f == 0 || std::isspace(userInput[f - 1])) && (f + word.size() >= userInput.size() || std::isspace(userInput[f + std::size(word)]))) {
				checkedResult |= typ;
				userInput.erase(f, word.size());
				f = 0;
			}

	return (userResponse)checkedResult;
}

int main()
{
	std::string reply;

	std::cout << "Enter reply: ";
	std::getline(std::cin, reply);

	switch (checkUserResponse(reply)) {
		case yes:
			std::cout << "Yes\n";
			break;

		case no:
			std::cout << "No\n";
			break;

		default:
			std::cout << getExp() << '\n';
			break;
	}
}

Last edited on
seeplus

I'm sorry if I'm driving you crazy :)

1) in line 76 you used word and typ, I cant seem to find where those are defined
2)I'm having trouble understanding what certain lines of the code are doing. I'm going to put in in comments. If you could clarify that would be great.

1
2
3
4
5
6
7
8
for (const auto& [word, typ] : res) //is this line running through a vector? if so which one? What is word and typ?
		for (size_t f = 0; (f = userInput.find(word, f)) != std::string::npos; ++f) 
			if (f + word.size() >= userInput.size() || std::isspace(userInput[f + std::size(word)])) //what is the purpose of what's before || and what is the isspace func (I couldn't find it when I looked it up)
{
				checkedResult |= typ;
				userInput.erase(f, word.size());
				f = 0;
			}


Thanks!
Last edited on
yes, its called a 'ranged based for loop' and it can loop over STL containers or anything with correct iterators. it says "for everything in res" ... word and typ are variables ... its using the multimap, not the vector, that he created... so word is a string and typ is a user response code, I believe. word and type are created for the scope of this for loop, just like for(int i -= 0; ...) creates a temporary 'i' variable.

is space ... boolean, is it a space? http://www.cplusplus.com/reference/cctype/isspace/ (all the "is" functions are boolean text helpers I believe)







jonnin, seeplus

I think I understood it! It's awesome!
I'm going to try and implement it...
syntax issue on lines 72,79 regarding the semicolen in the middle of the if statement.


The code is C++17 and needs to be compiled as C++17 (using whatever options your compiler uses). The statement before the ; is just an initialisation statement - like an initialisation statement in a for loop. This can also now be done with switch() as well.

The [word, typ] is also C++17 and is called a structured binding. When using auto and the initialising type is a struct/pair/tuple etc then each element can be assigned to its own variable with the correct type.

PS I'm slightly updated my code above as it was missing a condition.
Pages: 12