Gpseq
Last updated
Was this helpful?
Last updated
Was this helpful?
Что такое Gpseq?
Ссылки
Gpseq - это библиотека предоставляющая параллелизм для Vala и GObject.
Она содержит следующий функционал:
и managed blocking планировщик задач аналогичные планировщику Go.
Обработка данных в стиле функциональной парадигмы с поддержкой параллельного выполнения: эквивалент Java
параллелизм
Параллельная сортировка
64-битные атомарные операции
Переполнение безопасных арифметических функций для целых чисел со знаком
...
Еще не зарегистрирован на
Локальная версия
(TODO) pgi-docs (Python API)
(TODO) gjs-docs (JavaScript API)
Содержание
Future.of()
Future.err()
wait() and wait_until()
value
map()
flat_map()
light_map()
map_err()
then()
and_then()
zip()
transform()
Цепочка фьючерсов
Зачем использовать Gpseq.Future вместо Gee.Future?
Future - это read-only значение, которое становиться доступным в определенный момент. Значение (или исключение) обычно устанавливается с помощью Promise.
метод main() и using Gpseq;
опущены
Пример 1. Futures и promises
Вывод: 123
Вывод: Oops!
Future.of () - это вспомогательный метод, который возвращает Future
, заполненный заданным значением.
Вывод: 123
Future.err () - это вспомогательный метод, который возвращает Future
, заполненный заданным исключением.
Пример 2. wait()
Пример 3. wait_until()
Получает значение Future
Вывод: ERROR: Oops!
map - применяет функцию к каждомы елементу коллекции:
Работает как map, но с одним отличием — можно преобразовать один элемент в ноль, один или множество других.
Для того, чтобы один элемент преобразовать в ноль элементов, нужно вернуть null, либо пустой стрим. Чтобы преобразовать в один элемент, нужно вернуть Future из одного элемента, например, через Future.of(x). Для возвращения нескольких элементов, можно любыми способами создать Future с этими элементами.
light_map() - это облегченная версия map(). Данная функция может быть вычислена повторно в любое время.
Пример 4. map () против light_map ()
Вывод:
Функция then()
отложено запускает переданную в качестве аргумента функцию (принимающую future) - после вычисления текущего future, результатом чего может является значение или исключение.
Примечание автора перевода: Если по человечески -- С помощью функции then() можно создать цепочку ленивых вычислений, то есть поставить их в очередь, получая гарантию что следующие будут вычислены только после предыдущих.
Вывод:
exception .vala
Вывод:
Функция and_then()
отложено запускает переданную в качестве аргумента функцию (принимающую future) - после вычисления текущего future, результатом чего может является значение, а не исключение .
value.vala
output:
exception.vala
output: (none)
zip() объединяет значения двух фьючерсов в один.
Простой пример: zip [1,2,3] [3,2,1] - соеденяет списки [(1,3),(2,2),(3,1)]
output: 123
transform() создает новый future применяя переданную в качестве аргумента функцию к этому future, после того как этот future будет вычислен.
Все остальные mapping функции — map(), flat_map(), then(), etc. — кроме light_map() реализованы с помощью transform().
Вывод: 123
Вы можете поместить mapping методы в цепочку.
вывод: Hello 123!
Вывод:
Libgee уже предоставляет фьючерсы и промисы. Однако с некоторыми функциями есть проблемы, а некоторых не хватает.
Code refactoring may be required to solve the problems, and it will break backward compatibility of libgee. Instead of waiting for the problems to be fixed, I chose to implement Gpseq’s own Future.
Seq поддерживает различные методы обработки данных. В Seq есть 2 типа операций: промежуточных и терминальных. Seq последовательность может состоять из нуля и более промежуточных операций и терминальной операции.
Следовательно, конвейеры seq, содержащие промежуточные stateful операции, могут потребовать нескольких проходов или буферизации важных промежуточных вычислений. Напротив, конвейеры seq, содержащие только stateless операции, могут обрабатываться за один проход.
Stateless операции:
filter, flat_map, map, parallel, sequential, etc.
Stateful операции:
chop, chop_ordered, distinct, limit, limit_ordered, order_by, skip, skip_ordered, etc.
Терминальные операции могут изменять источник для получения результата или побочного эффекта. После выполнения терминальной операции seq последовательность закрывается и больше не может использоваться.
Терминальные операции:
foreach, all_match, any_match, collect, collect_ordered, count, find_any, find_first, fold, group_by, iterator, max, min, none_match, partition, reduce, spliterator, etc.
Промежуточные укороченные операции могут привести к тому что seq итерирующий по бесконечному источнику(например генератору четных чисел) приведет к результату за конечное время .
Short-circuiting операции:
all_match, any_match, chop, chop_ordered, find_any, find_first, limit, limit_ordered, none_match, etc.
При запуске терминальной операции конвейер seq выполняется последовательно или параллельно в зависимости от режима seq, в котором он вызывается. Последовательный режим является режимом по умолчанию. Он может быть изменен с помощью промежуточных операций sequential()
или parallel()
.
Все операции соблюдают порядок встречи элементов seq при последовательном выполнении. Однако при параллельном выполнении все промежуточные и терминальные операции с сохранением состояния могут не учитывать реальный порядок порядок элементов(на то они и паралельные), за исключением операций, определенных как явно упорядоченные, таких как find_first()
.
Main method, using Gpseq;
, and using Gee;
omitted.
output:
Промежуточный оператор filter отбирает только те строки, длина которых не превышает 3.
output:
Применяет функцию к каждому элементу и затем возвращает стрим, в котором элементами будут результаты функции. map можно применять для изменения типа элементов.
output:
Работает как map, но с одним отличием — можно преобразовать один элемент в ноль, один или множество других.
Для того, чтобы один элемент преобразовать в ноль элементов, нужно вернуть null
, либо пустой стрим. Чтобы преобразовать в один элемент, нужно вернуть стрим из одного элемента, например, через Seq.of(x). Для возвращения нескольких элементов, можно любыми способами создать стрим с этими элементами.
output:
Example 1. skip()
output:
Example 2. limit()
output:
Example 3. chop()
output:
They are the ordered version of skip/limit/chop. They always respect encounter order regardless of sequential/parallel execution. They are, however, quite expensive on parallel execution.
output:
output:
output:
Example 5. any_match()
output:
(!)
is the explicit non-null cast operator in Vala. (Currently not necessary, except when using --enable-experimental-non-null
option)
output:
Example 6. find_any()
output:
Example 7. find_first()
output:
Example 8. find_any() vs find_first() on parallel execution
output:
Each element is generated by the given next function applied to the previous element, and the initial element is the seed.Example 9. For loop
output:
Example 10. reduce()
output:
Example 11. fold()
output:
output:
output:
output:
You should read the documentation of Spliterator first.
output:
See the section Collectors.
Both perform a mutable reduction operation on the elements of a seq.collect()
collect() performs a concurrent reduction if the seq is in parallel mode and the collector is CONCURRENT.collect_ordered()
collect_ordered() preserves encounter order even though the seq is in parallel mode, if the collector is not CONCURRENT or not UNORDERED. If, however, the seq is in parallel mode and the collector is CONCURRENT and UNORDERED, it performs an unordered concurrent reduction.
Most seq operations take delegates which can throw errors. If an error occurs in the delegate, the returned future is completed with the error.Example 12. Error handling
output:
Main method, using Gpseq;
, using Gpseq.Collectors;
, and using Gee;
omitted.Example 13. Collectors.to_list()
output:
Example 14. Collectors.to_generic_array()
output:
GenericArray (Vala) == GPtrArray (C-lang)
Example 15. Collectors.sum_int()
output:
The arithmetic wraps around on overflow; e.g. the sum of int.MAX and 1 ⇒ int.MIN
Example 16. Collectors.count()
output:
Example 17. Collectors.join()with_default_delimiter.vala
output:
delimiter_specified.vala
output:
Example 18. Collectors.partition_with()
partition_with.vala
output:
Seq.partition(pred)
is equivalent to collect( partition<G>(pred) )
. And partition<G>(pred)
is equivalent to partition_with<Gee.List<G>,G>(pred, to_list<G>())
.
Example 19. Collectors.wrap()
output:
Example 20. Collectors.tee()
tee.vala
output:
A collector implements four methods: create_accumulator, accumulate, combine, and finish.
create_accumulator() - Creates a new accumulator, such as Gee.Collection.
accumulate() - Incorporates a new element into a accumulator.
combine() - Combines two accumulators into one.
finish() - Transforms the accumulator into a final result.
The methods must satisfy an identity and an associativity constraints. The identity constraint means that combining any accumulator with an empty accumulator must produce an equivalent result. i.e. an accumulator a must be equivalent to collector.accumulate(a, collector.create_accumulator())
.
The associativity constraint means that splitting the computation must produce an equivalent result. i.e.:Associativity constraint
Here is the collector implementation of Collectors.to_collection<G>():CollectionCollector.vala
TODO
Библиотека Gpseq предоставляет два вида объектов и .
Операции не блокируют текущий поток. Вместо этого большинство из них возвращают Future как результат. Он будет вычислен в качестве значения или исключения.
Если значение не ещё готово, "получающая" значение сторона будет находиться в заблокированном состоянии, пока значение не будет вычислено. Если Future
завершается исключением, получение значения вызовет .
Gee версия переопределяет исключения с помощью FutureError.EXCEPTION
в map функциях — map()
, flat_map()
, и zip()
. FutureError.EXCEPTION
вместо реального исключения не несет никакой информации и нежелателен.
Gee.Future не поддерживает цепочки вычислений фьючерсов. map()
on an already completed future doesn’t return and blocks the program.
Exceptions can’t be thrown in the map functions, and there is no way to map exceptions.
Compare with to see the difference.
это последовательность элементов, поддерживающая паралельные и последовательные операции. Эквивалент Java’s .
Промежуточные операции всегда выполняются . Это означает что вычисление seq не начнется пока не будет выполен хотя бы один терминальный оператор.
Промежуточные операции также делятся на те у которых есть состояния и те у которых их(stateless и stateful ). Операции без сохранения состояния не сохраняют состояние предыдущего элемента при обработке нового элемента. Поэтому каждый элемент может обрабатываться независимо от других элементов. В операциях с отслеживанием состояния(stateful) учитываются состояния предыдущих операций над элементами при обработке новых элементов, из-за чего обработка может быть выполнена только последовотельн(Например вычисление чисел Фибоначчи).
You should read the of first.Example 4. all_match()
is an object for traversing and partitioning elements of a data source, used in Seq.
is an object for mutable reduction operation that accumulates input elements into a mutable accumulator, and transforms it into a final result. It is used with collect()
or collect_ordered()
.
namespace provides various useful collector implementations. Here are some examples:
wrap() takes a collector and returns a collector will produce a object containing the result of the given collector.
Collectors also have a property, . it provides hints that can be used to optimize the operation.
More detailed description about seq pipelines can be found on the Seq documentations: ,