一个 C 解决方案,以表明正如 Norman Ramsey 所说,它非常乏味。它将过滤器作为带有上下文的回调,只是为了踢球,但在您的情况下,您可以将 0 作为过滤器数据和 not_wspc 作为过滤器:
int not_wspc(void *, char c) {
if isspace((unsigned char)c) return 0;
if ((c == '.') || (c == ',')) return 0;
return 1;
}
typedef struct {
char c;
int pos;
} charwithpos;
KGram *foo(const char *input, int (*filter)(void *,char), void *filterdata) {
size_t len = strlen(input);
charwithpos *filtered = malloc(len * sizeof(*filtered));
assert(filtered);
// combine Norman's zip and filter steps
charwithpos *current = filtered
for (size_t i = 0; i < len; ++i) {
if (filter(filterdata, input[i])) {
current->c = input[i];
current->pos = i;
++current;
}
}
size_t shortlen = (current - filtered);
// wouldn't normally recommend returning malloced data, but
// illustrates the point.
KGram *result = malloc((shortlen / 5 + 1) * sizeof(*result));
assert(result);
// take each 5 step
KGram *currentgram = result;
current = filtered;
for (size_t i = 0; i < shortlen; ++i) {
currentgram->text[i%5] = current->c;
if ((i % 5) == 0) {
currentgram->start = current->pos;
} else if ((i % 5) == 4) {
currentgram->end = current->pos;
++currentgram;
}
++current;
}
if (shortlen % 5) != 0 {
currentgram->end = filtered[shortlen-1].pos;
currentgram->text[shortlen%5] = 0;
}
free(filtered);
return(result);
}
或者类似的东西,我实际上无法编译和测试它。显然,这有一个明显的弱点,filtered 一次只能看到一个字符,这意味着它不能应用回溯算法。您可以通过将整个字符串传递给过滤器来绕过它,这样如果有必要,它可以在第一次调用时做很多工作,并存储结果以在所有其余调用中返回。但是,如果您需要将类似正则表达式的逻辑应用于任意类型,那么 C 可能不适合使用。
这是 C++ 解决方案的开端,甚至没有使用 <functional>。不确定 Norman 对 new 的语言有什么看法:仅仅因为该语言拥有它并不意味着您必须使用它 ;-)
template <typename OutputIterator>
struct KGramOutput {
OutputIterator dest;
KGram kgram;
KGramOutput(OutputIterator dest) : dest(dest) {}
void add(char, size_t);
void flush(void);
};
template <typename InputIterator, typename OutputIterator, typename Filter>
void foo(InputIterator first, InputIterator last, OutputIterator dest, Filter filter) {
size_t i = 0;
KGramOutput<OutputIterator> kgram(dest);
while (first != last) {
if (filter(*first)) kgram.add(*first, i);
++first;
++i;
}
kgram.flush();
}
add 和 flush 函数有点乏味,它们必须将 5 对绑定到一个 KGram 结构中,然后执行 *dest++ = kgram。例如,用户可以通过 vector<KGram> 传递 pushback_iterator 作为输出迭代器。顺便说一句,“5”和“char”也可以是模板参数。