paint-brush
SeqGen: la biblioteca que creé para la generación de secuenciasby@crazyrat
530
530

SeqGen: la biblioteca que creé para la generación de secuencias

crazyrat7m2023/12/15
Read on Terminal Reader

Una secuencia (informalmente hablando) es un conjunto de elementos (principalmente números) donde la producción de cada elemento se basa en los elementos anteriores. El ejemplo más básico es una secuencia lineal simple de números enteros positivos donde el primer elemento es 0 y el siguiente elemento es el elemento anterior más uno, por lo que podemos obtener el segundo elemento sumando uno al primero, y podemos obtener el tercero. elemento añadiendo uno al segundo, y así sucesivamente. La secuencia lineal quedaría así: {0, 1, 2, 3,…, n}.
featured image - SeqGen: la biblioteca que creé para la generación de secuencias
crazyrat HackerNoon profile picture

Introducción:

Si busca en Internet una biblioteca que pueda generar secuencias, difícilmente podrá encontrarla, aunque las secuencias son un concepto central de las matemáticas discretas y la informática.


En este breve artículo, veremos una biblioteca que escribí para la generación de secuencias llamada SeqGen .

¿Qué es una secuencia?

Una secuencia (informalmente hablando) es un conjunto de elementos (principalmente números) donde la producción de cada elemento se basa en los elementos anteriores.


El ejemplo más básico es una secuencia lineal simple de números enteros positivos donde el primer elemento es 0 y el siguiente elemento es el elemento anterior más uno, por lo que podemos obtener el segundo elemento sumando uno al primero, y podemos obtener el tercero. elemento añadiendo uno al segundo, y así sucesivamente. La secuencia lineal se vería así: {0, 1, 2, 3, …, n} .


Un ejemplo más complejo podría ser la secuencia de Fibonacci donde los dos primeros elementos son 0 y 1, y el siguiente elemento es la suma de los dos elementos anteriores. La secuencia de Fibonacci se vería así: {0, 1, 1, 2, 3, 5, 8, 13, 21, …, n}


Podemos notar desde arriba que una secuencia se define con dos propiedades:

  • Los elementos iniciales
  • Una función que produce el siguiente elemento.

Usando la biblioteca:

2.0_ Dependencias:

La biblioteca SeqGen está escrita en el lenguaje de programación Rust; Para realizar el seguimiento, es necesario tener instalado Rust .

2.1_ Creando un proyecto:

Creemos un nuevo proyecto para usar la biblioteca SeqGen; podemos hacerlo con carga:

 $ cargo new --bin sequence && cd sequence


Ahora, agreguemos la biblioteca como una dependencia a nuestro proyecto:

 $ cargo add seqgen


Ahora estamos listos para usar la biblioteca.

Creando una secuencia:

En SeqGen, el proceso de creación de secuencias se asigna directamente a las dos propiedades de la secuencia que concluimos en la sección ¿Qué es una secuencia? Tenemos que definir los elementos iniciales y la función que produce el siguiente elemento (llamada función de transición en SeqGen).


Creemos la secuencia lineal:

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


El tipo Sequence es una estructura que representa una secuencia; Al llamar a la función asociada new() en este tipo, obtenemos una nueva instancia que no está definida. En esta instancia indefinida, podemos llamar a métodos para definirla.


El primer método es initial_elements() que acepta un vector de elementos como argumento y lo establece como los elementos iniciales de la instancia.


El segundo método es transition_function() que toma como argumento un cierre que representa la transición de los elementos actualmente disponibles al siguiente elemento.


Este cierre tiene acceso a dos argumentos: el primero es alive_elements que representa los elementos disponibles actualmente, y el segundo es current_element_index (de tipo usize ) que es el índice del elemento actual en generación. Consulte la siguiente tabla para ver una ilustración de la función de transición.


Elemento actual en generación

elementos_vivos

índice_elemento_actual

1

[0]

1

2

[0, 1]

2

3

[0, 1, 2]

3

n

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

n


alive_elements es de tipo SequencePart , veremos el tipo SequencePart más adelante en este artículo.


Dado que el índice del elemento también es su valor en la secuencia lineal, podemos simplificar el ejemplo anterior a:

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


Aquí, no necesitamos definir los elementos iniciales y no necesitamos acceder a los elementos activos; solo necesitamos el índice (que en este caso se llama i ) y simplemente lo devolvemos.


De la misma manera podemos definir la secuencia 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 }); }

Dado que la secuencia de Fibonacci crece exponencialmente, generar más de 187 elementos con esta definición hará que u128 se desborde. Una mejor definición usaría big int en lugar de u128 .

Usando la secuencia :

Después de que definimos nuestra secuencia, podemos acceder a sus 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}"); }

Aquí, accedemos al elemento 111 utilizando el método nth_element() que devuelve una referencia inmutable al elemento ( &usize en este caso).


Observe que hicimos que linear_seq fuera mutable. Esto se debe a que el método nth_element() mutará los elementos vivos de la secuencia.


De esta forma, podemos acceder a cualquier elemento de la secuencia (desde el elemento con índice 0 hasta el elemento con índice usize::MAX .)


También podemos iterar sobre la secuencia como cualquier iterador de 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 imprimirá todos los elementos de la secuencia (desde el elemento 0 hasta el elemento usize::MAX ):

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


Podemos obtener los elementos impares de la secuencia usando el siguiente 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}")); }


Producción:

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


La secuencia que definimos es diferida, lo que significa que los elementos no se generarán a menos que sean necesarios (en caso de iteración) o se soliciten explícitamente (usando el método nth_element() ).

Trabajar con partes de la secuencia :

A veces, solo necesitamos trabajar con partes de una secuencia; en este caso, tenemos partes de secuencia.


Hay tres tipos de parte de secuencia:

  • AliveElementsPart
  • ImmutableRangePart
  • MutableRangePart


AliveElementsParte:

Podemos obtener los elementos activos usando el método alive_elements() en la secuencia:

 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 los elementos activos (de 0 a 110 en este caso porque generamos previamente 111 elementos).


Parte de rango inmutable:

Los rangos inmutables son rangos de elementos vivos; no pueden mutar la secuencia. Si crea un rango inmutable y no todos sus elementos están vivos, obtendrá un error (error DeadRange ).


Podemos crear un rango inmutable usando el método range() que devuelve un Result . La variante Ok es ImmutableRangePart y la variante Err es RangeError . El RangeError podría ser la variante InvalidRange (en caso de que el inicio del rango sea mayor que su final), o podría ser la variante DeadRange (en caso de que no todos los elementos del rango estén vivos):

 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 generará pánico con el error DeadRange porque no hay ningún elemento vivo. Podemos corregir esto con lo siguiente:

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

Aquí, generamos 3 elementos para que el rango sea válido.


Parte de rango mutable:


Un rango mutable puede mutar la secuencia (generar elementos).


Podemos usar un rango mutable como este:

 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á los elementos del 0 al 110.

Producción:

Gracias por leer este artículo hasta el final y espero que hayas encontrado algo útil en él. Si tiene alguna sugerencia que pueda mejorar esta biblioteca, abra una edición en GitHub y, si desea contribuir a la biblioteca , sería maravilloso.


También publicado aquí