Введение: Если вы поищите в Интернете библиотеку, способную генерировать последовательности, вы вряд ли ее найдете, хотя последовательности являются основной концепцией дискретной математики и информатики. В этой короткой статье мы рассмотрим библиотеку , которую я написал для генерации последовательностей. 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 1 [0] 1 2 [0, 1] 2 3 [0, 1, 2] 3 n [0, 1, 2, 3, …, n] n имеет тип , мы рассмотрим тип позже в этой статье. 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 элементов с этим определением приведет к переполнению . Лучшее определение будет использовать big int вместо . u128 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 внести свой вклад в библиотеку Также опубликовано здесь