我实际上想知道什么是在C ++中正确地将字母分配给范围的最佳方法。
例如,我们有这样的规模:
我们可以用最简单的方式完成任务:
if(a < 40)
return 'T';
else if(a < 55)
return 'D';
else if(a < 70)
return 'P';
else if(a < 80)
return 'A';
else if(a < 90)
return 'E';
else if(a <= 100)
return 'O';
但是,你有更好的想法吗?
当我们有更大的数字和更多的字母时(我认为如果陈述仍然令人讨厌......)?或者如果范围之间存在空闲空间,例如30-40 45-55 60-70?
答案 0 :(得分:4)
您可以使用简单数组来排序范围和输出
char getGrade (int grade) {
int upper[] = { 40, 55, 70, 80, 90, 100 };
int lower[] = { 0, 40, 55, 70, 80, 90 };
char grades[] = { 'T', 'D', 'P', 'A', 'E', 'O' };
for (int i = 0; i< 6; i++)
if ((grade< upper[i]) && (grade >= lower[i]))
return grades[i];
return 'X'; // no grade assigned
}
编辑:我正在使用@YSC建议的struct
和std::find_if
添加有趣的实现
#include <iostream>
#include <algorithm>
#include <vector>
struct Data{ int lower; int upper; char grade; };
char getGrade (int grade, std::vector<Data> data) {
auto it = std::find_if(
data.begin(),
data.end(),
[&grade](Data d) { return (d.lower<= grade) && (d.upper > grade); }
);
if (it == data.end())
return 'X'; // not found
else
return it->grade;
}
int main () {
const std::vector<Data> myData = { { 0, 40, 'T'} , { 40, 55, 'D'}, {55, 70, 'P'}, {70, 80, 'A'}, {80, 90, 'E'}, {90, 101, 'O'} };
std::cout << getGrade(20, myData) << std::endl;
return 0;
}
答案 1 :(得分:3)
几乎在所有情况下都适用的一般解决方案是以自描述的方式定义每个符号的边界,然后使用std::lower_bound
找到适当的值:
#include <iostream>
#include <algorithm>
char grade(int a)
{
constexpr std::pair<int, char> bounds[] = {
{ 39, 'T' },
{ 54, 'D' },
{ 69, 'P' },
{ 79, 'A' },
{ 89, 'E' },
{ 99, 'O' },
{ 100, '\0' },
};
return std::lower_bound(begin(bounds),end(bounds),std::make_pair(a, '\0'))->second;
}
grade(39): T
grade(40): D
grade(54): D
grade(55): P
grade(69): P
grade(70): A
grade(79): A
grade(80): E
grade(89): E
grade(90): O
grade(99): O
grade(100):
针对此特定问题的另一个解决方案可能是,因为此处涉及的数字很小,所以使用简单的查找表:
static const char map[] =
"............" // make 40 of them
"TTTTTTTTTTTTTTT" // 40..54
"DDDDDDDDDDDDDDD" // 55..69
// etc
;
return map[a];
最后,对于较大或负数或较稀疏范围的替代方法是使用实际的std::map
:
#include <map>
static const std::map<int, char> map = {
{ 40, 'T' },
{ 41, 'T' },
// ...
{ 100, 'O' }
};
return map.at(a);
答案 2 :(得分:3)
这是C ++ 14的答案。一切都可以翻译成C ++ 11,不那么漂亮。
template<class F, class Base=std::less<>>
auto order_by( F&& f, Base&& b={} ) {
return
[f=std::forward<F>(f), b = std::forward<Base>(b)]
(auto const& lhs, auto const& rhs)
->bool
{
return b( f(lhs), f(rhs) );
};
}
order_by
接受一个投影和一个比较函数对象,并返回一个比较函数对象,该对象将投影应用于std::less<>
或比较函数对象。
这在排序或搜索时很有用,因为C ++算法需要比较函数对象,而投影很容易编写。
template<class A, class B>
struct binary_overload_t:A,B{
using A::operator();
using B::operator();
binary_overload_t( A a, B b ):A(std::move(a)), B(std::move(b)) {}
};
template<class A, class B>
binary_overload_t< A, B >
binary_overload( A a, B b ) {
return { std::move(a), std::move(b) };
}
binary_overload
允许您重载函数对象。
template<class T>
struct valid_range_t {
T start, finish;
};
这表示有效范围。我可以使用std::pair
,但我更喜欢有意义的类型。
template<class T, class V>
struct ranged_value_t {
valid_range_t<T> range;
V value;
};
template<class T, class It>
auto find_value( It begin, It end, T const& target )
-> decltype(std::addressof( begin->value ))
{
// project target into target
// and a ranged value onto the lower end of the range
auto projection = binary_overload(
[]( auto const& ranged )->T const& {
return ranged.range.finish;
},
[]( T const& t )->T const& {
return t;
}
);
//
auto it = std::upper_bound( begin, end,
target,
order_by( projection )
);
if (it == end) return nullptr;
if (target < it->range.start) return nullptr;
return std::addressof( it->value );
}
现在find_value
将一对迭代器带到排列有非重叠范围的ranged_value_t
类型结构。
然后返回一个指向第一个(也就是唯一的)值的条目的指针,该值的(半开)范围包含target
。
ranged_value_t<int, char> table[]={
{{0,40}, 'T'},
{{41,55}, 'D'},
{{56,70}, 'P'},
{{71,80}, 'A'},
{{81,90}, 'E'},
{{91,101}, 'O'}
};
auto* ptr = find_value( std::begin(table), std::end(table), 83 );
if (ptr) std::cout << *ptr << "\n"; else std::cout << "nullptr\n";
这个答案相对于替代方案的优势:
find_value
函数只接受迭代器(并且更喜欢它们是随机访问)。valid_range_t
添加一个标记(或使用可选项),并在我们检查find_value
时在s
中使用该标记,并将构造函数添加到valid_range_t
以使其成为易于使用。增加它以支持半开和间隔时间需要一些工作。作为第二次检查,我很想把它变成find_value
。
重叠间隔也需要一些工作。我会在开始时(s
)执行lower_bound,在结束时执行upper_bound(f
)。
我发现这种东西最适合数据驱动设计;在C ++代码中对此进行硬编码是一个糟糕的计划。相反,您使用配置,并编写代码以进行验证并由该配置驱动。
答案 3 :(得分:2)
如果你想要想要一个易于扩展的列表,那么你可以使用查找表并循环遍历它:
#include <iostream>
#include <utility>
#include <vector>
char returnGrade(int a)
{
std::vector<std::pair<char, int>> chars = {
std::make_pair('T', 40),
std::make_pair('D', 55),
std::make_pair('P', 70),
std::make_pair('A', 80),
std::make_pair('E', 90),
std::make_pair('O', 100)
};
for(auto itr = chars.begin(); itr != chars.end(); ++itr)
{
if(a < itr->second)
return itr->first;
}
//return the last one if we passed the loop.
return chars.back().first;
}
int main() {
//test it..
std::cout << returnGrade(20) << " " << returnGrade(45) << " " << returnGrade(90) << " " << returnGrade(100);
return 0;
}
当然,你不应该在函数的本地查找表,但你明白了。
答案 4 :(得分:0)
不,您可以使用简单array
和简单for
int array[ 8 ] = { 0, 40, 55, 70, 80, 90, 100, 101 };
char score[ 7 ] = { 'T', 'D', 'P', 'A', 'E', 'O', 'O' };
int input = 50;
for( int index = 0; index < 8; ++index ){
if( input < array[ index ] ){
std::cout << score[ index - 1 ]; // D
break;
}
}
注意
std:cout
return
O
和101
也匹配100本身。