Решение C, чтобы показать, что, как говорит Норман Рэмси, это довольно утомительно. Он принимает фильтр как обратный вызов с контекстом, только для ударов, но в вашем случае вы можете передать 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>
. Не уверен, что Норман говорит о языках с 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
. Пользователь может передать, например, pushback_iterator
через vector<KGram>
в качестве выходного итератора. Кстати, '5' и 'char' также могут быть параметрами шаблона.