Si vous recherchez sur Internet une bibliothèque capable de générer des séquences, vous pourrez difficilement en trouver une, bien que les séquences soient un concept central des mathématiques discrètes et de l'informatique.
Dans ce court article, nous examinerons une bibliothèque que j'ai écrite pour la génération de séquences appelée SeqGen .
Une séquence (de manière informelle) est un ensemble d'éléments (principalement des nombres) où la production de chaque élément est basée sur le ou les éléments précédents.
L'exemple le plus basique est une simple séquence linéaire d'entiers positifs où le premier élément est 0 et l'élément suivant est l'élément précédent plus un, nous pouvons donc obtenir le deuxième élément en ajoutant un au premier, et nous pouvons obtenir le troisième. élément en en ajoutant un au deuxième, et ainsi de suite. La séquence linéaire ressemblerait à ceci : {0, 1, 2, 3, …, n}
.
Un exemple plus complexe pourrait être la séquence de Fibonacci où les deux premiers éléments sont 0 et 1, et l'élément suivant est la somme des deux éléments précédents. La séquence de Fibonacci ressemblerait à ceci : {0, 1, 1, 2, 3, 5, 8, 13, 21, …, n}
On peut remarquer ci-dessus qu'une séquence est définie avec deux propriétés :
2.0_
Dépendances :La bibliothèque SeqGen est écrite dans le langage de programmation Rust ; pour faire un suivi, vous devez avoir installé Rust .
2.1_
Créer un projet :Créons un nouveau projet pour utiliser la bibliothèque SeqGen ; nous pouvons le faire avec du fret :
$ cargo new --bin sequence && cd sequence
Maintenant, ajoutons la bibliothèque en tant que dépendance à notre projet :
$ cargo add seqgen
Maintenant, nous sommes prêts à utiliser la bibliothèque.
Dans SeqGen, le processus de création de séquence correspond directement aux deux propriétés de la séquence que nous avons conclues dans la section Qu'est-ce qu'une séquence ; nous devons définir les éléments initiaux et la fonction qui produit l'élément suivant (appelée fonction de transition dans SeqGen).
Créons la séquence linéaire :
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 }); }
Le type Sequence
est une structure qui représente une séquence ; en appelant la fonction associée new()
sur ce type, on obtient une nouvelle instance non définie. Sur cette instance non définie, nous pouvons appeler des méthodes pour la définir.
La première méthode est initial_elements()
qui accepte un vecteur d'éléments comme argument et le définit comme éléments initiaux de l'instance.
La deuxième méthode est transition_function()
qui prend comme argument une fermeture qui représente la transition des éléments actuellement disponibles vers l'élément suivant.
Cette fermeture a accès à deux arguments : le premier est alive_elements
qui représente les éléments actuellement disponibles, et le second est current_element_index
(de type usize
) qui est l'index de l'élément courant en génération. Voir le tableau ci-dessous pour une illustration de la fonction de transition.
Élément actuel dans la génération | éléments_vivants | index_élément_actuel |
---|---|---|
| | |
| | |
| | |
| | |
alive_elements
est de type SequencePart
, nous examinerons le type SequencePart
plus loin dans cet article
Puisque l'indice de l'élément est également sa valeur dans la séquence linéaire, nous pouvons simplifier l'exemple ci-dessus comme suit :
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); }
Ici, nous n'avons pas besoin de définir les éléments initiaux, et nous n'avons pas besoin d'accéder aux éléments actifs ; nous avons juste besoin de l'index (qui est nommé i
dans ce cas), et nous le renvoyons simplement.
De la même manière, on peut définir la suite 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 }); }
Étant donné que la séquence de Fibonacci croît de façon exponentielle, générer plus de 187 éléments avec cette définition entraînera un débordement de u128
. Une meilleure définition utiliserait big int au lieu de u128
.
Après avoir défini notre séquence, nous pouvons accéder à ses éléments :
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}"); }
Ici, nous accédons au 111ème élément en utilisant la méthode nth_element()
qui renvoie une référence immuable à l'élément ( &usize
dans ce cas).
Notez que nous avons rendu linear_seq
mutable. En effet, la méthode nth_element()
va muter les éléments vivants de la séquence.
De cette façon, nous pouvons accéder à n'importe quel élément de la séquence (de l'élément avec un indice de 0
à l'élément avec un indice de usize::MAX
.)
Nous pouvons également parcourir la séquence comme n’importe quel itérateur Rust :
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); linear_seq.for_each(|e| println!("{e}")); }
Ce code imprimera tous les éléments de la séquence (de l'élément 0
à l'élément usize::MAX
):
$ cargo run -q 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
Nous pouvons récupérer les éléments impairs de la séquence en utilisant le code suivant :
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}")); }
Sortir:
$ cargo run -q 1 3 5 7 9 11 13 ...
La séquence que nous définissons est paresseuse, ce qui signifie que les éléments ne seront générés que si cela est nécessaire (en cas d'itération) ou explicitement demandé (en utilisant la méthode nth_element()
).
Parfois, nous devons travailler uniquement avec des parties d’une séquence, dans ce cas, nous avons des parties de séquence.
Il existe trois types de partie séquence :
AliveElementsPart
ImmutableRangePart
MutableRangePart
Partie AliveElements :
Nous pouvons récupérer les éléments live en utilisant la méthode alive_elements()
sur la séquence :
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} "); } }
Ce code imprimera tous les éléments vivants (0 à 110 dans ce cas car nous avons pré-généré 111 éléments).
ImmutableRangePart :
Les plages immuables sont des plages d'éléments vivants ; ils ne peuvent pas muter la séquence. Si vous créez une plage immuable et que tous ses éléments ne sont pas vivants, vous obtiendrez une erreur (erreur DeadRange
).
Nous pouvons créer une plage immuable en utilisant la méthode range()
qui renvoie un Result
. La variante Ok
est ImmutableRangePart
et la variante Err
est RangeError
. RangeError
pourrait être la variante InvalidRange
(dans le cas où le début de la plage est supérieur à sa fin), ou il pourrait s'agir de la variante DeadRange
(dans le cas où tous les éléments de la plage ne sont pas vivants) :
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}") } }
Ce code paniquera avec l'erreur DeadRange
car il n'y a aucun élément vivant. Nous pouvons corriger cela avec ce qui suit :
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}") } }
Ici, nous avons généré 3 éléments pour rendre la plage valide.
MutableRangePart :
Une plage mutable peut muter la séquence (générer des éléments).
Nous pouvons utiliser une plage mutable comme ceci :
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}"); } }
Ce code imprimera les éléments de 0 à 110.
Merci d'avoir lu cet article jusqu'au bout et j'espère que vous y avez trouvé quelque chose d'utile. Si vous avez des suggestions susceptibles d'améliorer cette bibliothèque, veuillez ouvrir un numéro sur GitHub , et si vous souhaitez contribuer à la bibliothèque , ce serait merveilleux.
Également publié ici