paint-brush
SeqGen: библиотека, которую я создал для генерации последовательностейк@crazyrat
560 чтения
560 чтения

SeqGen: библиотека, которую я создал для генерации последовательностей

к crazyrat7m2023/12/15
Read on Terminal Reader

Слишком долго; Читать

Последовательность (неформально говоря) — это набор элементов (в основном чисел), в котором создание каждого элемента основано на предыдущем элементе(ах). Самый простой пример — это простая линейная последовательность целых положительных чисел, где первый элемент равен 0, а следующий элемент — это предыдущий элемент плюс один, поэтому мы можем получить второй элемент, добавив единицу к первому, и мы можем получить третий элемент. элемент, добавив один ко второму и так далее. Линейная последовательность будет выглядеть так: {0, 1, 2, 3, …, n}.
featured image - SeqGen: библиотека, которую я создал для генерации последовательностей
crazyrat HackerNoon profile picture

Введение:

Если вы поищите в Интернете библиотеку, способную генерировать последовательности, вы вряд ли ее найдете, хотя последовательности являются основной концепцией дискретной математики и информатики.


В этой короткой статье мы рассмотрим библиотеку 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 элементов с этим определением приведет к переполнению 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 . Вариант OkImmutableRangePart , а вариант ErrRangeError . 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 , и если вы хотите внести свой вклад в библиотеку , это было бы замечательно.


Также опубликовано здесь