我有:
const char kLetters[] = "QWERTYUIOPASDFGHJKLZXCVBNM";
我可以调用kLetters[n]
在O(1)时间内获取键盘字母的第n个字母。但是,我将不得不遍历kLetter(取O(n)或至少O(log n))时间进行反向查找。
我想使用模板创建一个反向查找表作为编译时静态查找表,并且想知道是否有这样做的方法。
编辑 - 如评论中所述,反向查找意味着我提供'E'并返回2.此外,我的字母表示例不是最好的示例,我不想对订单做任何假设。因此我将字母表更改为键盘顺序。答案 0 :(得分:5)
这样的事情怎么样?它允许您指定范围而不是完整的字符串。
#include <iostream>
template <int Start, int End, int N>
struct lookup {
static_assert(Start != End, "Can't have 0 length lookup table");
enum { value = lookup<Start+(Start < End ? 1:-1),End,N-1>::value };
};
template <int Start, int End>
struct lookup<Start,End,0> {
enum { value = Start };
};
template <int Start, int End, int V, int P=0>
struct reverse_lookup {
static_assert(Start != End, "V isn't in the range Start, End");
static_assert(Start != End || !P, "Can't have 0 length range");
enum { value = reverse_lookup<Start+(Start < End ? 1:-1),End,V,P+1>::value };
};
template <int Start, int End, int P>
struct reverse_lookup<Start,End,Start,P> {
enum { value = P };
};
int main() {
std::cout << char(lookup<'A', 'Z', 3>::value) << std::endl;
std::cout << char(lookup<'Z', 'A', 3>::value) << std::endl;
std::cout << int(reverse_lookup<'A','Z','F'>::value) << std::endl;
}
答案 1 :(得分:3)
好的,在知道反向查询后,我认为你可以这样做:
const char kLetters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int get_index(char letter)
{
return letter - 'A';
}
毕竟,字母A
位于0
索引,B
位于1
,C
位于2
......所以上。这提供了足够的暗示。
到目前为止,其他解决方案适用于非任意字母序列,而@awoodland解决方案假定要获取其索引的字母在编译时已知使它不那么有用。
但是这个解决方案试图解决这两个限制;也就是说,它应该有效:
使用任意字母序列,例如
const char Letters[] = "ZBADCEWFVGHIUXJTKSLYQMROPN";
编译时字母可能是 unknown 。获取索引的函数具有以下签名:
int Index(char letter);
以下是使用@DavidRodríguez在his blog中描述的技术的完整代码:
#include <iostream>
const char Letters[] = "ZBADCEWFVGHIUXJTKSLYQMROPN";
template<char L> int Index();
template<> int Index<'Z'>() { return 0; }
template<> int Index<'B'>() { return 1; }
template<> int Index<'A'>() { return 2; }
template<> int Index<'D'>() { return 3; }
template<> int Index<'C'>() { return 4; }
template<> int Index<'E'>() { return 5; }
template<> int Index<'W'>() { return 6; }
template<> int Index<'F'>() { return 7; }
template<> int Index<'V'>() { return 8; }
template<> int Index<'G'>() { return 9; }
template<> int Index<'H'>() { return 10; }
template<> int Index<'I'>() { return 11; }
template<> int Index<'U'>() { return 12; }
template<> int Index<'X'>() { return 13; }
template<> int Index<'J'>() { return 14; }
template<> int Index<'T'>() { return 15; }
template<> int Index<'K'>() { return 16; }
template<> int Index<'S'>() { return 17; }
template<> int Index<'L'>() { return 18; }
template<> int Index<'Y'>() { return 19; }
template<> int Index<'Q'>() { return 20; }
template<> int Index<'M'>() { return 21; }
template<> int Index<'R'>() { return 22; }
template<> int Index<'O'>() { return 23; }
template<> int Index<'P'>() { return 24; }
template<> int Index<'N'>() { return 25; }
typedef int (*fptr)();
const int limit = 26;
fptr indexLookup[ limit ];
template <char L>
struct init_indexLookup {
static void init( fptr *indexLookup ) {
indexLookup[ L - 'A' ] = &Index<L>;
init_indexLookup<L-1>::init( indexLookup );
}
};
template <>
struct init_indexLookup<'A'> {
static void init( fptr *indexLookup ) {
indexLookup[ 0 ] = &Index<'A'>;
}
};
const int ignore = (init_indexLookup<'Z'>::init(indexLookup),0);
int Index(char letter)
{
return indexLookup[letter-'A']();
}
这是测试代码:
int main()
{
std::cout << Index('A') << std::endl;
std::cout << Index('Z') << std::endl;
std::cout << Index('B') << std::endl;
std::cout << Index('K') << std::endl;
}
输出:
2
0
1
16
嗯,这实际上是两个函数调用:一个到Index()
,另一个来自indexLookup
中的一个。您可以通过编写(ideone)
int main()
{
std::cout << indexLookup['A'-'A']() << std::endl;
std::cout << indexLookup['Z'-'A']() << std::endl;
std::cout << indexLookup['B'-'A']() << std::endl;
std::cout << indexLookup['K'-'A']() << std::endl;
}
这看起来很麻烦,但是嘿,我们可以Index()
内联:
inline int Index(char letter)
{
return indexLookup[letter-'A']();
}
看起来很好,现在很可能编译器会使它等同于一个函数调用!
等待。我刚刚意识到整个解决方案简化为一个初始化为:
的查找表 const int indexLookup[] = {2,1,4,3,5,7,9,10,11,14,16,18,21,
25,23,24,20,22,17,15,12,8,6,13,19,0};
inline int Index(char letter)
{
return indexLookup[letter-'A'];
}
看起来简直令人难以置信!
答案 2 :(得分:1)
如果你可以使用Boost并且只需要编译时查找:
using namespace boost::mpl;
typedef vector_c<char, 'A', 'B', 'C', 'D'> Chars;
// lookup by index:
std::cout << at_c<Chars, 1>::type::value << std::endl; // B
// lookup by value:
typedef find<Chars, integral_c<char, 'C'> >::type Iter;
std::cout << Iter::pos::value << std::endl; // 2
答案 3 :(得分:0)
这假定'Z'> 'A',但不假设字母是连续的。 (虽然如果它们需要更少的内存)我很想放入if (numrLetters>26)
条件,所以一个聪明的编译器可以使用加法而不是表格用于ASCII,但后来决定我不想放慢代码不太聪明的编译器。
const char kLetters[] = "ABCDEFGHJJKLMNOPQRSTUVWXYZ";
const int numLetters = sizeof(kLetters);
const char rkLetters['Z'-'A'] = {};
const int numrLetters = sizeof(rkLetters);
struct LetterInit {
LetterInit() {
for(int i=0; i<numLetters; ++i)
rkLetters[kLetters[i]-'A'] = i;
}
}LetterInitInst;
char findChar(int index) {
assert(index>=0 && index<numLetters);
return kLetters[index];
}
int findIndex(char letter) {
assert(letter>='A' && letter<='Z');
return rkLetters[letter-'A'];
}
答案 4 :(得分:0)
由于有几个解决方案不生成表但仍允许编译时查找,这里是另一个
constexpr char kLetters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
constexpr int get(char const x, int const i = 0) {
return kLetters[i] == x ? i : get(x, i + 1);
}
在编译时使用
int x[get('F')];
static_assert(sizeof(x) == sizeof(int[5]), "");
指定不存在的字符将导致错误。如果在运行时使用该函数,则在指定不存在的字符时将获得未定义的行为。可以为这些情况添加适当的检查。
它产生找到的第一个字符的索引。如果角色在大海捞针中出现两次,则不会给出错误。
答案 5 :(得分:-1)
如果您可以使用c ++ 0x(使用gcc 4.5测试),则可以使用:
#include<initializer_list>
#include<iostream>
#include<map>
constexpr int getLetterNumber(char a){ return std::map<char,int>({{'a',2},{'b',1},{'c',4}})[a]; }
int main(){
const char ch='b';
std::cout<<ch<<": "<<getLetterNumber(ch)<<std::endl;
}
constexpr
在编译时执行评估。
编辑:正如所指出的,该解决方案不正确。 constexpr
不会影响编译时评估。这确实在编译时进行查找(类似于同时发布的解决方案)。
#include<iostream>
template<char C> int ch2Num();
#define CHR(c,i) template<> int ch2Num<c>(){ return i; }
CHR('a',2); CHR('b',1); /* ... */
#undef CHR
int main(void){
const char ch='b';
std::cout<<ch<<": "<<ch2Num<ch>()<<std::endl;
};