我在这里做什么有点不知所措。我想让某些按键序列执行某些操作。
我基本上需要存储最后N次击键,当按下某个键时,查找与最近一次击键相匹配的序列。
所以说我有2个序列:
yes
no
当我输入时,我的按键历史如下:
a
ab
abc
abcn
abcno
此时它应识别序列no
并执行相应的操作。
还需要使用以下序列:
year
yell
并输入如:
yeayell
键序列的长度是有限的,因此可以使用类似循环缓冲区的东西丢弃旧键击,在这种情况下,最佳大小为3。
我的击键用Keys
枚号表示。
我应该使用哪些数据结构或算法来存储最后N次击键并在最后找到序列?
答案 0 :(得分:1)
这是一个概念验证,允许您使用任何字符序列集合。我假设你只是匹配字符(而不是其他键,如Keys.Left
)。
// Initialize the collection of strings to be matched against here.
string[] stringSequences = new string[] { "yes", "no", "hello" };
int maxLength = stringSequences.Max(s => s.Length);
// The buffer to hold the sequence of the last N characters.
string buffer = "";
while (true)
{
// Read the next character, and append it to the end of the buffer.
ConsoleKeyInfo next = Console.ReadKey();
buffer += next.KeyChar;
// If the buffer has exceeded our maximum length,
// trim characters from its start.
if (buffer.Length > maxLength)
buffer = buffer.Substring(1);
// Check whether the last n characters of the buffer
// correspond to any of the sequences.
string match = stringSequences.FirstOrDefault(s => buffer.EndsWith(s));
if (match != null)
{
// Match! Perform any custom processing here.
Console.WriteLine(Environment.NewLine + "Match: " + match);
}
}
修改:适合使用密钥。
我无法轻易针对Keys
进行测试,因此我与ConsoleKey
进行了对话;但是,翻译代码应该不会太难。
// Initialize the collection of key sequences to be matched against here.
ConsoleKey[][] keysSequences = new ConsoleKey[][]
{
new ConsoleKey[] { ConsoleKey.Y, ConsoleKey.E, ConsoleKey.S },
new ConsoleKey[] { ConsoleKey.N, ConsoleKey.O },
new ConsoleKey[] { ConsoleKey.H, ConsoleKey.E, ConsoleKey.L, ConsoleKey.L, ConsoleKey.O },
};
int maxLength = keysSequences.Max(ks => ks.Length);
// The buffer to hold the sequence of the last N keys.
List<ConsoleKey> buffer = new List<ConsoleKey>();
while (true)
{
// Read the next key, and append it to the end of the buffer.
ConsoleKeyInfo next = Console.ReadKey();
buffer.Add(next.Key);
// If the buffer has exceeded our maximum length,
// trim keys from its start.
if (buffer.Count > maxLength)
buffer.RemoveAt(0);
// Check whether the last n keys of the buffer
// correspond to any of the sequences.
ConsoleKey[] match = keysSequences.FirstOrDefault(ks =>
buffer.Skip(buffer.Count - ks.Length).SequenceEqual(ks));
if (match != null)
{
// Match! Perform any custom processing here.
Console.WriteLine(Environment.NewLine + "Match: " +
string.Concat(match.Select(k => k.ToString()).ToArray()));
}
}
答案 1 :(得分:0)
简单的state machine应该效果很好。
它可以重置任何不遵守规则的输入。
enum States
{
initial,
y,
e,
s,
n,
o
}
if(char == 'n' && state == states.Initial)
{
state = States.n;
}
if(char == 'o' && state == states.n)
{
state = States.o;
}
... // etc for y, e, s - resetting to `Initial` where needed
... // Check for states o or s
答案 2 :(得分:0)
您可以使用循环缓冲区:
char[] buf = new char[3];
int pos = 0;
// on key press
buf[pos] = key;
if (buf[pos] == 'o' && buf[(pos + 2) % 3] == 'n')
No();
if (buf[pos] == 's' && buf[(pos + 2) % 3] == 'e' && buf[(pos + 1) % 3] == 'y')
Yes();
pos = (pos + 1) % 3;
答案 3 :(得分:0)
快速而简单的方法是使用Rolling hash
答案 4 :(得分:0)
不一定是最佳的,但也许是最简单和可重复使用的:
创建CircularBuffer<T>
通用容器。
这在构造函数N
中占用一个参数,即缓冲区中元素的最大数量。
该类有一个T
数组N
元素和一个索引变量i
,保存最后一个添加值的索引,将其初始化为-1。
CircularBuffer
有两种方法,Add(T
)和ToString(
)。
Add
方法增加i
,如果0
= i
将其包装到N
,并将值存储在数组中的适当位置。这应该允许您向缓冲区添加值,同时仅将N
个值保留在内存中。
ToString
将输出一个等于缓冲区中存储的字符串。
对于N
= 4,假设你有这个数组:
a b c d
the indices are 0 1 2 3
the last added value is ^
`ToString` should return 'dabc'
此处的包装逻辑是用户的练习。
每按一次键,将其添加到循环缓冲区,调用其ToString
方法,看它是否包含您寻找的序列。