paint-brush
SeqGen: a biblioteca que criei para geração de sequênciaspor@crazyrat
560 leituras
560 leituras

SeqGen: a biblioteca que criei para geração de sequências

por crazyrat7m2023/12/15
Read on Terminal Reader

Muito longo; Para ler

Uma sequência (falando informalmente) é um conjunto de elementos (principalmente números) onde a produção de cada elemento é baseada no(s) elemento(s) anterior(es). O exemplo mais básico é uma sequência linear simples de inteiros positivos onde o primeiro elemento é 0 e o próximo elemento é o elemento anterior mais um, então podemos obter o segundo elemento adicionando um ao primeiro, e podemos obter o terceiro elemento adicionando um ao segundo e assim por diante. A sequência linear ficaria assim: {0, 1, 2, 3,…, n}.
featured image - SeqGen: a biblioteca que criei para geração de sequências
crazyrat HackerNoon profile picture

Introdução:

Se você pesquisar na Internet por uma biblioteca que possa gerar sequências, dificilmente encontrará uma, embora as sequências sejam um conceito central da matemática discreta e da ciência da computação.


Neste breve artigo, daremos uma olhada em uma biblioteca que escrevi para a geração de sequência chamada SeqGen .

O que é uma sequência?

Uma sequência (falando informalmente) é um conjunto de elementos (principalmente números) onde a produção de cada elemento é baseada no(s) elemento(s) anterior(es).


O exemplo mais básico é uma sequência linear simples de inteiros positivos onde o primeiro elemento é 0 e o próximo elemento é o elemento anterior mais um, então podemos obter o segundo elemento adicionando um ao primeiro, e podemos obter o terceiro elemento adicionando um ao segundo e assim por diante. A sequência linear ficaria assim: {0, 1, 2, 3, …, n} .


Um exemplo mais complexo poderia ser a sequência de Fibonacci, onde os dois primeiros elementos são 0 e 1, e o próximo elemento é a soma dos dois elementos anteriores. A sequência de Fibonacci ficaria assim: {0, 1, 1, 2, 3, 5, 8, 13, 21, …, n}


Podemos notar acima que uma sequência é definida com duas propriedades:

  • Os elementos iniciais
  • Uma função que produz o próximo elemento

Usando a Biblioteca:

2.0_ Dependências:

A biblioteca SeqGen é escrita na linguagem de programação Rust; para acompanhar, você precisa ter o Rust instalado .

2.1_ Criando um Projeto:

Vamos criar um novo projeto para utilizar a biblioteca SeqGen; podemos fazer isso com carga:

 $ cargo new --bin sequence && cd sequence


Agora, vamos adicionar a biblioteca como dependência ao nosso projeto:

 $ cargo add seqgen


Agora estamos prontos para usar a biblioteca.

Criando uma sequência:

No SeqGen, o processo de criação de sequência é mapeado diretamente para as duas propriedades da sequência que concluímos na seção O que é uma sequência; temos que definir os elementos iniciais e a função que produz o próximo elemento (chamada de função de transição no SeqGen).


Vamos criar a sequência linear:

 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 }); }


O tipo Sequence é uma estrutura que representa uma sequência; chamando a função associada new() neste tipo, obtemos uma nova instância que é indefinida. Nesta instância indefinida, podemos chamar métodos para defini-la.


O primeiro método é initial_elements() que aceita um vetor de elementos como argumento e o define como os elementos iniciais da instância.


O segundo método é transition_function() que toma como argumento um encerramento que representa a transição dos elementos atualmente disponíveis para o próximo elemento.


Este encerramento tem acesso a dois argumentos: o primeiro é alive_elements que representa os elementos atualmente disponíveis, e o segundo é current_element_index (do tipo usize ) que é o índice do elemento atual em geração. Veja a tabela abaixo para uma ilustração da função de transição.


Elemento Atual na Geração

elementos_vivos

índice_elemento_atual

1

[0]

1

2

[0, 1]

2

3

[0, 1, 2]

3

n

[0, 1, 2, 3, …, n]

n


alive_elements é do tipo SequencePart , daremos uma olhada no tipo SequencePart posteriormente neste artigo


Como o índice do elemento também é o seu valor na sequência linear, podemos simplificar o exemplo acima para:

 use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); }


Aqui, não precisamos definir os elementos iniciais e não precisamos de acesso aos elementos ativos; precisamos apenas do índice (que é denominado i neste caso) e simplesmente o retornamos.


Da mesma forma, podemos definir a sequência de Fibonacci:

 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 }); }

Como a sequência de Fibonacci cresce exponencialmente, gerar mais de 187 elementos com esta definição fará com que u128 estoure. Uma definição melhor usaria big int em vez de u128 .

Usando a sequência :

Após definirmos nossa sequência, podemos acessar seus elementos:

 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}"); }

Aqui, estamos acessando o 111º elemento usando o método nth_element() que retorna uma referência imutável ao elemento ( &usize neste caso).


Observe que tornamos linear_seq mutável. Isso ocorre porque o método nth_element() irá transformar os elementos vivos da sequência.


Desta forma, podemos acessar qualquer elemento da sequência (desde o elemento com índice 0 até o elemento com índice usize::MAX .)


Também podemos iterar sobre a sequência como qualquer iterador Rust:

 use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); linear_seq.for_each(|e| println!("{e}")); }


Este código irá imprimir todos os elementos da sequência (do elemento 0 ao elemento usize::MAX ):

 $ cargo run -q 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ...


Podemos obter os elementos ímpares da sequência usando o seguinte código:

 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}")); }


Saída:

 $ cargo run -q 1 3 5 7 9 11 13 ...


A sequência que definimos é lenta, o que significa que os elementos não serão gerados a menos que sejam necessários (em caso de iteração) ou solicitados explicitamente (usando o método nth_element() ).

Trabalhando com partes da sequência :

Às vezes precisamos trabalhar apenas com partes de uma sequência, neste caso temos partes de sequência.


Existem três tipos de parte de sequência:

  • AliveElementsPart
  • ImmutableRangePart
  • MutableRangePart


AliveElementsParte:

Podemos obter os elementos vivos usando o método alive_elements() na sequência:

 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} "); } }

Este código imprimirá todos os elementos vivos (0 a 110 neste caso porque pré-geramos 111 elementos).


FaixaImutávelParte:

As faixas imutáveis são as faixas dos elementos vivos; eles não podem alterar a sequência. Se você criar um intervalo imutável e nem todos os seus elementos estiverem ativos, você receberá um erro (erro DeadRange ).


Podemos criar um intervalo imutável usando o método range() que retorna um Result . A variante Ok é ImmutableRangePart e a variante Err é RangeError . O RangeError pode ser a variante InvalidRange (caso o início do intervalo seja maior que o final) ou pode ser a variante DeadRange (caso nem todos os elementos do intervalo estejam ativos):

 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}") } }


Este código entrará em pânico com o erro DeadRange porque não há nenhum elemento ativo. Podemos corrigir isso com o seguinte:

 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}") } }

Aqui, geramos 3 elementos para tornar o intervalo válido.


MutableRangePart:


Um intervalo mutável pode alterar a sequência (gerar elementos).


Podemos usar um intervalo mutável assim:

 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}"); } }


Este código imprimirá os elementos de 0 a 110.

Outradução:

Obrigado por ler este artigo até o fim e espero que você tenha encontrado algo útil nele. Se você tiver alguma sugestão que possa melhorar esta biblioteca, abra um issue no GitHub , e se quiser contribuir com a biblioteca , isso seria maravilhoso.


Também publicado aqui