Конкурентный фильтр и фильтр-сведение
Функции QtConcurrent::filter(), QtConcurrent::filtered() и QtConcurrent::filteredReduced() фильтруют элементы в последовательности, такой как QList, параллельно. QtConcurrent::filter() изменяет последовательность на месте, QtConcurrent::filtered() возвращает новую последовательность, содержащую отфильтрованные данные, а QtConcurrent::filteredReduced() возвращает единственный результат.
Эти функции являются частью фреймворка Qt Concurrent.
Каждая из вышеперечисленных функций имеет блокирующую версию, которая возвращает конечный результат вместо QFuture. Вы используете их так же, как и асинхронные варианты.
QStringList strings = ...; // each call blocks until the entire operation is finished QStringList lowerCaseStrings = QtConcurrent::blockingFiltered(strings, allLowerCase); QtConcurrent::blockingFilter(strings, allLowerCase); QSet<QString> dictionary = QtConcurrent::blockingFilteredReduced(strings, allLowerCase, addToDictionary);
Обратите внимание, что типы результатов выше не являются объектами QFuture, а реальными типами результатов (в этом случае QStringList и QSet<QString>).
Конкурентный фильтр
QtConcurrent::filtered() принимает входную последовательность и функцию фильтрации. Эта функция фильтрации затем вызывается для каждого элемента в последовательности, и возвращается новая последовательность, содержащая отфильтрованные значения.
Функция фильтрации должна иметь следующий вид:
bool function(const T &t);
T должен соответствовать типу, хранящемуся в последовательности. Функция возвращает true если элемент следует сохранить, false — если его следует отбросить.
Этот пример демонстрирует, как сохранить строки, которые состоят только из строчных букв, из QStringList:
bool allLowerCase(const QString &string)
{
return string.lowered() == string;
}
QStringList strings = ...;
QFuture<QString> lowerCaseStrings = QtConcurrent::filtered(strings, allLowerCase); Результаты фильтра доступны через QFuture. См. документацию QFuture и QFutureWatcher для получения дополнительной информации о том, как использовать QFuture в ваших приложениях.
Если вы хотите изменить последовательность на месте, используйте QtConcurrent::filter():
QStringList strings = ...; QFuture<void> future = QtConcurrent::filter(strings, allLowerCase);
Поскольку последовательность изменяется на месте, QtConcurrent::filter() не возвращает никаких результатов через QFuture. Однако вы по-прежнему можете использовать QFuture и QFutureWatcher для отслеживания состояния фильтра.
Конкурентный фильтр-сведение
QtConcurrent::filteredReduced() аналогичен QtConcurrent::filtered(), но вместо возвращения последовательности с отфильтрованными результатами результаты объединяются в одно значение с помощью функции сведения.
Функция сведения должна иметь следующий вид:
V function(T &result, const U &intermediate)
T — тип конечного результата, U — тип фильтруемых элементов. Обратите внимание, что возвращаемое значение и возвращаемый тип функции сведения не используются.
Вызовите QtConcurrent::filteredReduced() так:
void addToDictionary(QSet<QString> &dictionary, const QString &string)
{
dictionary.insert(string);
}
QStringList strings = ...;
QFuture<QSet<QString>> dictionary = QtConcurrent::filteredReduced(strings, allLowerCase, addToDictionary); Функция сведения будет вызываться один раз для каждого результата, сохранённого функцией фильтра, и должна объединять промежуточный результат в переменную результат. QtConcurrent::filteredReduced() гарантирует, что только один поток будет вызывать reduce за раз, поэтому использование мьютекса для блокировки переменной результата не требуется. Перечисление QtConcurrent::ReduceOptions предоставляет способ управления порядком выполнения сведения.
Дополнительные возможности API
Использование итераторов вместо последовательности
Каждая из вышеуказанных функций имеет вариант, который принимает диапазон итераторов вместо последовательности. Вы используете их так же, как и варианты с последовательностями:
QStringList strings = ...; QFuture<QString> lowerCaseStrings = QtConcurrent::filtered(strings.constBegin(), strings.constEnd(), allLowerCase); // filter in-place only works on non-const iterators QFuture<void> future = QtConcurrent::filter(strings.begin(), strings.end(), allLowerCase); QFuture<QSet<QString>> dictionary = QtConcurrent::filteredReduced(strings.constBegin(), strings.constEnd(), allLowerCase, addToDictionary);
Использование методов-членов
QtConcurrent::filter(), QtConcurrent::filtered() и QtConcurrent::filteredReduced() принимают указатели на методы-члены. Тип класса метода-члена должен соответствовать типу, хранящемуся в последовательности:
// keep only images with an alpha channel
QList<QImage> images = ...;
QFuture<void> alphaImages = QtConcurrent::filter(images, &QImage::hasAlphaChannel);
// retrieve gray scale images
QList<QImage> images = ...;
QFuture<QImage> grayscaleImages = QtConcurrent::filtered(images, &QImage::isGrayscale);
// create a set of all printable characters
QList<QChar> characters = ...;
QFuture<QSet<QChar>> set = QtConcurrent::filteredReduced(characters, qOverload<>(&QChar::isPrint),
qOverload<const QChar&>(&QSet<QChar>::insert)); Обратите внимание на использование qOverload. Оно необходимо для разрешения неоднозначности для методов, имеющих несколько перегрузок.
Также обратите внимание, что при использовании QtConcurrent::filteredReduced() вы можете свободно смешивать использование обычных и методов-членов:
// can mix normal functions and member functions with QtConcurrent::filteredReduced()
// create a dictionary of all lower cased strings
extern bool allLowerCase(const QString &string);
QStringList strings = ...;
QFuture<QSet<QString>> lowerCase = QtConcurrent::filteredReduced(strings, allLowerCase,
qOverload<const QString&>(&QSet<QString>::insert));
// create a collage of all gray scale images
extern void addToCollage(QImage &collage, const QImage &grayscaleImage);
QList<QImage> images = ...;
QFuture<QImage> collage = QtConcurrent::filteredReduced(images, &QImage::isGrayscale, addToCollage); Использование объектов-функций
QtConcurrent::filter(), QtConcurrent::filtered() и QtConcurrent::filteredReduced() принимают объекты-функции в качестве функции фильтрации. Эти объекты-функции могут быть использованы для добавления состояния к вызову функции:
struct StartsWith
{
StartsWith(const QString &string)
: m_string(string) { }
bool operator()(const QString &testString)
{
return testString.startsWith(m_string);
}
QString m_string;
};
QList<QString> strings = ...;
QFuture<QString> fooString = QtConcurrent::filtered(strings, StartsWith(QLatin1String("Foo"))); Для функции сведения объекты-функции напрямую не поддерживаются. Однако объекты-функции могут быть использованы, когда тип результата сведения явно указан:
struct StringTransform
{
void operator()(QString &result, const QString &value);
};
QFuture<QString> fooString =
QtConcurrent::filteredReduced<QString>(strings,
StartsWith(QLatin1String("Foo")),
StringTransform()); Использование лямбда-выражений
QtConcurrent::filter(), QtConcurrent::filtered() и QtConcurrent::filteredReduced() принимают лямбда-выражения в качестве функции фильтрации и сведения:
// keep only even integers
QList<int> list { 1, 2, 3, 4 };
QtConcurrent::blockingFilter(list, [](int n) { return (n & 1) == 0; });
// retrieve only even integers
QList<int> list2 { 1, 2, 3, 4 };
QFuture<int> future = QtConcurrent::filtered(list2, [](int x) {
return (x & 1) == 0;
});
QList<int> results = future.results();
// add up all even integers
QList<int> list3 { 1, 2, 3, 4 };
int sum = QtConcurrent::filteredReduced<int>(list3,
[](int x) {
return (x & 1) == 0;
},
[](int &sum, int x) {
sum += x;
}
); При использовании QtConcurrent::filteredReduced() или QtConcurrent::blockingFilteredReduced() вы можете свободно смешивать использование обычных функций, методов-членов и лямбда-выражений.
void intSumReduce(int &sum, int x)
{
sum += x;
}
QList<int> list { 1, 2, 3, 4 };
int sum = QtConcurrent::filteredReduced(list,
[] (int x) {
return (x & 1) == 0;
},
intSumReduce
); Для функции сведения лямбда-выражения напрямую не поддерживаются. Однако лямбда-выражения могут быть использованы, когда тип результата сведения явно указан:
bool keepEvenIntegers(int x)
{
return (x & 1) == 0;
}
QList<int> list { 1, 2, 3, 4 };
int sum = QtConcurrent::filteredReduced<int>(list,
keepEvenIntegers,
[](int &sum, int x) {
sum += x;
}
); Обертывание функций, принимающих несколько аргументов
Если вы хотите использовать функцию фильтрации, которая принимает более одного аргумента, вы можете использовать лямбда-функцию или std::bind() для преобразования её в функцию, принимающую один аргумент.
В качестве примера мы используем QString::contains():
bool QString::contains(const QRegularExpression ®exp) const;
QString::contains() принимает 2 аргумента (включая указатель "this") и не может быть использована с QtConcurrent::filtered() напрямую, так как QtConcurrent::filtered() ожидает функцию, принимающую один аргумент. Чтобы использовать QString::contains() с QtConcurrent::filtered(), нам необходимо предоставить значение для аргумента regexp:
QStringList strings = ...;
QFuture<QString> future = QtConcurrent::filtered(list, [](const QString &str) {
return str.contains(QRegularExpression("^\\S+$")); // matches strings without whitespace
});
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-6.2/qtconcurrentfilter.html