Если вы поищите в Интернете библиотеку, способную генерировать последовательности, вы вряд ли ее найдете, хотя последовательности являются основной концепцией дискретной математики и информатики.
В этой короткой статье мы рассмотрим библиотеку SeqGen , которую я написал для генерации последовательностей.
Последовательность (неформально говоря) — это набор элементов (в основном чисел), в котором создание каждого элемента основано на предыдущем элементе(ах).
Самый простой пример — это простая линейная последовательность целых положительных чисел, где первый элемент равен 0, а следующий элемент — это предыдущий элемент плюс один, поэтому мы можем получить второй элемент, добавив единицу к первому, и мы можем получить третий элемент. элемент, добавив один ко второму и так далее. Линейная последовательность будет выглядеть так: {0, 1, 2, 3, …, n}
.
Более сложным примером может быть последовательность Фибоначчи, где первые два элемента равны 0 и 1, а следующий элемент представляет собой сумму двух предыдущих элементов. Последовательность Фибоначчи будет выглядеть так: {0, 1, 1, 2, 3, 5, 8, 13, 21, …, n}
Мы можем заметить выше, что последовательность определена с двумя свойствами:
2.0_
Зависимости:Библиотека SeqGen написана на языке программирования Rust; для продолжения вам необходимо установить Rust .
2.1_
Создание проекта:Давайте создадим новый проект для использования библиотеки SeqGen; мы можем сделать это с грузом:
$ cargo new --bin sequence && cd sequence
Теперь давайте добавим библиотеку в качестве зависимости к нашему проекту:
$ cargo add seqgen
Теперь мы готовы использовать библиотеку.
В SeqGen процесс создания последовательности напрямую отображается на два свойства последовательности, которые мы пришли к выводу в разделе «Что такое последовательность»; нам нужно определить начальные элементы и функцию, создающую следующий элемент (называемую функцией перехода в SeqGen).
Создадим линейную последовательность:
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new() .initial_elements(vec![0]) .transition_function(|alive_elements, current_element_index| { alive_elements.last_element().unwrap() + 1 }); }
Тип Sequence
— это структура, представляющая последовательность; вызывая связанную функцию new()
для этого типа, мы получаем новый экземпляр, который не определен. В этом неопределенном экземпляре мы можем вызвать методы для его определения.
Первый метод — initial_elements()
, который принимает вектор элементов в качестве аргумента и устанавливает его в качестве начальных элементов экземпляра.
Второй метод transition_function()
, который принимает в качестве аргумента замыкание, представляющее переход от доступных в данный момент элементов к следующему элементу.
Это замыкание имеет доступ к двум аргументам: первый — alive_elements
, который представляет доступные в данный момент элементы, а второй — current_element_index
(типа usize
), который является индексом текущего элемента в генерации. В таблице ниже представлена иллюстрация функции перехода.
Текущий элемент в генерации | живые_элементы | current_element_index |
---|---|---|
| | |
| | |
| | |
| | |
alive_elements
имеет тип SequencePart
, мы рассмотрим тип SequencePart
позже в этой статье.
Поскольку индекс элемента также является его значением в линейной последовательности, мы можем упростить приведенный выше пример до:
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); }
Здесь нам не нужно определять исходные элементы и нам не нужен доступ к действующим элементам; нам просто нужен индекс (в данном случае он называется i
), и мы просто возвращаем его.
Таким же образом мы можем определить последовательность Фибоначчи:
use seqgen::prelude::*; fn main() { let fib_seq = Sequence::new() .initial_elements(vec![0, 1_u128]) .transition_function(|alive_elements, i| { let x = alive_elements.nth_element(i - 1).unwrap(); let y = alive_elements.nth_element(i - 2).unwrap(); x + y }); }
Поскольку последовательность Фибоначчи растет экспоненциально, создание более 187 элементов с этим определением приведет к переполнению u128
. Лучшее определение будет использовать big int вместо u128
.
После того, как мы определили нашу последовательность, мы можем получить доступ к ее элементам:
use seqgen::prelude::*; fn main() { let mut linear_seq = Sequence::new().transition_function(|_, i| i); let some_element = linear_seq.nth_element(111); println!("{some_element}"); }
Здесь мы получаем доступ к 111-му элементу с помощью метода nth_element()
, который возвращает неизменяемую ссылку на элемент (в данном случае &usize
).
Обратите внимание, что мы сделали linear_seq
изменяемым. Это потому, что метод nth_element()
будет изменять живые элементы последовательности.
Таким образом, мы можем получить доступ к любому элементу последовательности (от элемента с индексом 0
до элемента с индексом usize::MAX
.)
Мы также можем перебирать последовательность, как любой итератор Rust:
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); linear_seq.for_each(|e| println!("{e}")); }
Этот код напечатает все элементы последовательности (от элемента 0
до элемента usize::MAX
):
$ cargo run -q 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
Мы можем получить нечетные элементы из последовательности, используя следующий код:
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); let odd_elements = linear_seq.filter(|e| e % 2 != 0); odd_elements.for_each(|e| println!("{e}")); }
Выход:
$ cargo run -q 1 3 5 7 9 11 13 ...
Последовательность, которую мы определяем, является ленивой, то есть элементы не будут генерироваться, если они не нужны (в случае итерации) или явно не запрошены (с использованием метода nth_element()
).
Иногда нам нужно работать только с частями последовательности, в данном случае у нас есть части последовательности.
Существует три типа части последовательности:
AliveElementsPart
ImmutableRangePart
MutableRangePart
ЖивыеЭлементыЧасть:
Мы можем получить живые элементы, используя метод alive_elements()
в последовательности:
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new() .transition_function(|_, i| i) .pre_generate(111); let alive_elements = linear_seq.alive_elements(); for alive_element in alive_elements { print!("{alive_element} "); } }
Этот код напечатает все активные элементы (в данном случае от 0 до 110, поскольку мы заранее сгенерировали 111 элементов).
ИммутаблеРангеЧасть:
Неизменяемые диапазоны — это диапазоны живых элементов; они не могут изменить последовательность. Если вы создадите неизменяемый диапазон и не все его элементы живы, вы получите ошибку (ошибка DeadRange
).
Мы можем создать неизменяемый диапазон, используя метод range()
, который возвращает Result
. Вариант Ok
— ImmutableRangePart
, а вариант Err
— RangeError
. RangeError
может быть вариантом InvalidRange
(в случае, если начало диапазона больше его конца) или вариантом DeadRange
(в случае, если не все элементы диапазона активны):
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); let range = linear_seq.range(0, 3).unwrap(); for e in range { println!("{e}") } }
Этот код будет паниковать с ошибкой DeadRange
, поскольку нет живого элемента. Мы можем исправить это следующим образом:
use seqgen::prelude::*; fn main() { let mut linear_seq = Sequence::new().transition_function(|_, i| i); linear_seq.generate(3); let range = linear_seq.range(0, 3).unwrap(); for e in range { println!("{e}") } }
Здесь мы сгенерировали 3 элемента, чтобы сделать диапазон действительным.
МутабельРангеЧасть:
Изменяемый диапазон может изменять последовательность (генерировать элементы).
Мы можем использовать изменяемый диапазон следующим образом:
use seqgen::prelude::*; fn main() { let mut linear_seq = Sequence::new().transition_function(|_, i| i); let mut_range = linear_seq.range_mut(0, 111).unwrap(); for e in mut_range { println!("{e}"); } }
Этот код напечатает элементы от 0 до 110.
Спасибо, что дочитали эту статью до конца, надеюсь, вы нашли в ней что-то полезное. Если у вас есть какие-либо предложения по улучшению этой библиотеки, откройте вопрос на GitHub , и если вы хотите внести свой вклад в библиотеку , это было бы замечательно.
Также опубликовано здесь