如何将范围映射到值?
我实际上想知道什么是在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?
您可以使用排序的区间简单数组和输出
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
}
编辑:我添加有趣的实施与struct
和std::find_if
通过@YSC建议
#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;
}
对于这样小的数字,我会使用一个查找表:
static const char map[] =
"............" // make 40 of them
"TTTTTTTTTTTTTTT" // 40..54
"DDDDDDDDDDDDDDD" // 55..69
// etc
;
return map[a];
一种替代对于较大或负数或sparsier范围是使用实际std::map
:
#include <map>
static const std::map<int, char> map = {
{ 40, 'T' },
{ 41, 'T' },
// ...
{ 100, 'O' }
};
return map.at(a);
LogicStuff提出的更可读的解决方案是定义您的界限并通过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(
std::begin(bounds),
std::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):
一组\ *节点\ *上的'std :: lower_bound'如何? – LogicStuff
'std :: lower_bound'对我来说是新事物,不错 – BartekPL
@LogicStuff我已将您的建议添加到我的答案中。这是你在想什么? – YSC
如果你想成为幻想和希望有一个容易膨胀的名单,那么你可以用通过它查找表,只是循环:
#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;
}
中当然,你不应该有该函数本地的查找表,但你明白了。
但是如果范围之间有空闲空间,例如: 30-40 45-55 60-70,它回到我的代码的以前版本。我想知道是否可以用一些聪明的方式使用地图。 – BartekPL
@BartekPL大概你需要返回_something_作为缺口(例如41..44),所以添加_something_作为地图的一个元素('std :: make_pair(_something_,41)')。 – TripeHound
@TripeHound,是的,我知道这种方式,这个代码似乎是不错的选择 – BartekPL
没有,当你可以使用简单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自身。
这是一个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
。
重叠间隔也需要一些工作。我会在开始时执行lower_bound(s
),并在结束时执行upper_bound(f
)。
我觉得这种东西最适合数据驱动设计;在C++代码中硬编码是一个糟糕的计划。相反,您需要使用配置,然后编写代码进行验证并由该配置驱动。
我最喜欢的矫枉过正的类型 –
如果你需要超过一个小时构思查找表,你需要两个稍后再维护它,上周雇用的新人会在某个时候讨厌你。大多数时候,简单笨拙比聪明美丽更好。 – YSC
它是这个或查找表,只有6个记录,但这种方法似乎没问题。可能要重构幻数 –
您不必与下一个条件与前一个的否定。还有'else',它不需要在那里,因为有'return'。 – LogicStuff
@LogicStuff也许是为了清楚。 –