[英]Storing the last N keystrokes
在这里做什么我有点不知所措。 我希望某些键击序列执行某些操作。
我基本上需要存储最后N次击键,当按下某个键时,查找与最近的击键相匹配的序列。
所以说我有2个序列:
yes
no
当我输入时,我的击键历史如下所示:
a
ab
abc
abcn
abcno
此时它应识别序列no
并执行适当的操作。
它还需要使用以下序列:
year
yell
和输入如:
yeayell
密钥序列具有有限长度,因此可以使用类似循环缓冲区的东西丢弃旧的击键,在这种情况下,最佳大小为3。
我的击键用Keys
枚号表示。
我应该使用哪些数据结构或算法来存储最后N次击键并在最后找到序列?
这是一个概念验证,允许您使用任何字符序列集合。 我假设你只匹配字符(而不是其他键,如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()));
}
}
一个简单的状态机应该运行良好。
它可以重置任何不遵守规则的输入。
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
您可以使用循环缓冲区:
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;
快速而简单的方法是使用Rolling hash
不一定是最优的,但也许是最简单和可重用的:
创建一个CircularBuffer<T>
泛型容器。
这在构造函数中采用一个参数N
,即缓冲区中元素的最大数量。
该类有一个带有N
元素的T
数组和一个索引变量i
,它保存最后一个添加值的索引,将其初始化为-1。
CircularBuffer
有两个方法, Add(T
)和ToString(
)。
如果i
= N
,则Add
方法递增i
,将其包装为0
并将值存储在数组中的适当位置。 这应该允许您向缓冲区添加值,同时仅在内存中保留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
方法,看它是否包含您寻找的序列。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.