Wenn Sie im Internet nach einer Bibliothek suchen, die Sequenzen generieren kann, werden Sie kaum eine finden, obwohl Sequenzen ein Kernkonzept der diskreten Mathematik und Informatik sind.
In diesem kurzen Artikel werfen wir einen Blick auf eine Bibliothek namens SeqGen , die ich für die Sequenzgenerierung geschrieben habe.
Eine Sequenz (informell gesprochen) ist eine Menge von Elementen (hauptsächlich Zahlen), wobei die Produktion jedes Elements auf dem/den vorhergehenden Element(en) basiert.
Das einfachste Beispiel ist eine einfache lineare Folge positiver Ganzzahlen, bei der das erste Element 0 und das nächste Element das vorhergehende Element plus eins ist. Wir können also das zweite Element erhalten, indem wir eins zum ersten addieren, und wir können das dritte Element erhalten Element, indem man eins zum zweiten hinzufügt, und so weiter. Die lineare Folge würde so aussehen: {0, 1, 2, 3, …, n}
.
Ein komplexeres Beispiel könnte die Fibonacci-Folge sein, bei der die ersten beiden Elemente 0 und 1 sind und das nächste Element die Summe der beiden vorhergehenden Elemente ist. Die Fibonacci-Folge würde so aussehen: {0, 1, 1, 2, 3, 5, 8, 13, 21, …, n}
Wir können oben erkennen, dass eine Sequenz mit zwei Eigenschaften definiert ist:
2.0_
Abhängigkeiten:Die SeqGen-Bibliothek ist in der Programmiersprache Rust geschrieben; Um weiterzumachen, muss Rust installiert sein.
2.1_
Ein Projekt erstellen:Lassen Sie uns ein neues Projekt erstellen, um die SeqGen-Bibliothek zu verwenden. Wir können dies mit Fracht tun:
$ cargo new --bin sequence && cd sequence
Fügen wir nun die Bibliothek als Abhängigkeit zu unserem Projekt hinzu:
$ cargo add seqgen
Jetzt können wir die Bibliothek nutzen.
In SeqGen wird der Sequenzerstellungsprozess direkt den beiden Eigenschaften der Sequenz zugeordnet, die wir im Abschnitt „Was ist eine Sequenz“ festgestellt haben; Wir müssen die Anfangselemente und die Funktion definieren, die das nächste Element erzeugt (in SeqGen Übergangsfunktion genannt).
Erstellen wir die lineare Folge:
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 }); }
Der Sequence
Typ ist eine Struktur, die eine Sequenz darstellt. Durch den Aufruf der zugehörigen Funktion new()
für diesen Typ erhalten wir eine neue Instanz, die undefiniert ist. Für diese undefinierte Instanz können wir Methoden aufrufen, um sie zu definieren.
Die erste Methode ist initial_elements()
, die einen Vektor von Elementen als Argument akzeptiert und ihn als Anfangselemente der Instanz festlegt.
Die zweite Methode ist transition_function()
, die als Argument einen Abschluss verwendet, der den Übergang von den aktuell verfügbaren Elementen zum nächsten Element darstellt.
Dieser Abschluss hat Zugriff auf zwei Argumente: Das erste ist alive_elements
, das die aktuell verfügbaren Elemente darstellt, und das zweite ist current_element_index
(vom Typ usize
), das der Index des aktuellen Elements in der Generierung ist. Eine Darstellung der Übergangsfunktion finden Sie in der folgenden Tabelle.
Aktuelles Element in der Generation | lebendige_elemente | aktueller_element_index |
---|---|---|
| | |
| | |
| | |
| | |
alive_elements
ist vom Typ SequencePart
. Wir werden uns den Typ SequencePart
später in diesem Artikel ansehen
Da der Index des Elements auch sein Wert in der linearen Folge ist, können wir das obige Beispiel wie folgt vereinfachen:
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); }
Hier müssen wir die anfänglichen Elemente nicht definieren und wir benötigen keinen Zugriff auf die Live-Elemente. Wir brauchen nur den Index (der in diesem Fall i
heißt) und geben ihn einfach zurück.
Auf die gleiche Weise können wir die Fibonacci-Folge definieren:
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 }); }
Da die Fibonacci-Folge exponentiell wächst, führt die Generierung von mehr als 187 Elementen mit dieser Definition zu einem Überlauf von u128
. Eine bessere Definition würde big int anstelle von u128
verwenden.
Nachdem wir unsere Sequenz definiert haben, können wir auf ihre Elemente zugreifen:
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}"); }
Hier greifen wir auf das 111. Element mit der Methode nth_element()
zu, die einen unveränderlichen Verweis auf das Element zurückgibt (in diesem Fall &usize
).
Beachten Sie, dass wir linear_seq
veränderbar gemacht haben. Das liegt daran, dass die Methode nth_element()
die aktiven Elemente der Sequenz mutiert.
Auf diese Weise können wir auf jedes Element der Sequenz zugreifen (vom Element mit dem Index 0
bis zum Element mit dem Index usize::MAX
).
Wir können die Sequenz auch wie jeder Rust-Iterator durchlaufen:
use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); linear_seq.for_each(|e| println!("{e}")); }
Dieser Code gibt alle Elemente der Sequenz aus (vom Element 0
bis zum Element usize::MAX
):
$ cargo run -q 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
Mit dem folgenden Code können wir die ungeraden Elemente aus der Sequenz abrufen:
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}")); }
Ausgabe:
$ cargo run -q 1 3 5 7 9 11 13 ...
Die von uns definierte Sequenz ist verzögert, was bedeutet, dass die Elemente nur dann generiert werden, wenn sie benötigt werden (im Falle einer Iteration) oder explizit angefordert werden (mit der Methode nth_element()
).
Manchmal müssen wir nur mit Teilen einer Sequenz arbeiten, in diesem Fall handelt es sich um Sequenzteile.
Es gibt drei Arten des Sequenzteils:
AliveElementsPart
ImmutableRangePart
MutableRangePart
AliveElementsPart:
Wir können die Live-Elemente mit der Methode alive_elements()
für die Sequenz abrufen:
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} "); } }
Dieser Code gibt alle aktiven Elemente aus (in diesem Fall 0 bis 110, da wir 111 Elemente vorab generiert haben).
ImmutableRangePart:
Unveränderliche Bereiche sind Bereiche der lebendigen Elemente; Sie können die Sequenz nicht mutieren. Wenn Sie einen unveränderlichen Bereich erstellen und nicht alle seine Elemente aktiv sind, erhalten Sie eine Fehlermeldung ( DeadRange
Fehler).
Mit der Methode range()
können wir einen unveränderlichen Bereich erstellen, der ein Result
zurückgibt. Die Ok
Variante ist ImmutableRangePart
und die Err
Variante ist RangeError
. Der RangeError
könnte die InvalidRange
Variante sein (falls der Anfang des Bereichs größer als sein Ende ist), oder er könnte DeadRange
Variante sein (falls nicht alle Elemente des Bereichs aktiv sind):
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}") } }
Dieser Code löst einen DeadRange
Fehler aus, da kein aktives Element vorhanden ist. Wir können dies wie folgt korrigieren:
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}") } }
Hier haben wir drei Elemente generiert, um den Bereich gültig zu machen.
MutableRangePart:
Ein veränderlicher Bereich kann die Sequenz verändern (Elemente generieren).
Wir können einen veränderlichen Bereich wie folgt verwenden:
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}"); } }
Dieser Code druckt die Elemente von 0 bis 110.
Vielen Dank, dass Sie diesen Artikel bis zum Ende gelesen haben, und ich hoffe, Sie haben etwas Nützliches darin gefunden. Wenn Sie Vorschläge zur Verbesserung dieser Bibliothek haben, öffnen Sie bitte ein Problem auf GitHub . Wenn Sie zur Bibliothek beitragen möchten, wäre das wunderbar.
Auch hier veröffentlicht