paint-brush
SeqGen: Die Bibliothek, die ich für die Sequenzgenerierung erstellt habevon@crazyrat
560 Lesungen
560 Lesungen

SeqGen: Die Bibliothek, die ich für die Sequenzgenerierung erstellt habe

von crazyrat7m2023/12/15
Read on Terminal Reader

Zu lang; Lesen

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}.
featured image - SeqGen: Die Bibliothek, die ich für die Sequenzgenerierung erstellt habe
crazyrat HackerNoon profile picture

Einführung:

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.

Was ist eine Sequenz?

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:

  • Die ersten Elemente
  • Eine Funktion, die das nächste Element erzeugt

Nutzung der Bibliothek:

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.

Erstellen einer Sequenz:

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

1

[0]

1

2

[0, 1]

2

3

[0, 1, 2]

3

n

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

n


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.

Verwendung der Sequenz :

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() ).

Arbeiten mit Teilen der Sequenz :

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.

Einführung:

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