導入: シーケンスを生成できるライブラリをインターネットで検索すると、シーケンスは離散数学とコンピューター サイエンスの中核概念であるにもかかわらず、ほとんど見つかりません。 この短い記事では、 と呼ばれるシーケンス生成用に私が作成したライブラリを見ていきます。 SeqGen シーケンスとは何ですか? シーケンス (非公式に言うと) は、各要素の生成が先行する要素に基づいて行われる要素 (主に数値) のセットです。 最も基本的な例は、最初の要素が 0 で、次の要素が前の要素に 1 を加えたものである正の整数の単純な線形シーケンスです。したがって、最初の要素に 1 を加算することで 2 番目の要素を取得でき、3 番目の要素を取得できます。 2 番目の要素に 1 を追加するなどして要素を追加します。線形シーケンスは次のようになります: 。 {0, 1, 2, 3, …, n} より複雑な例は、最初の 2 つの要素が 0 と 1 で、次の要素が前の 2 つの要素の合計であるフィボナッチ数列です。フィボナッチ数列は次のようになります: {0, 1, 1, 2, 3, 5, 8, 13, 21, …, n} 上記のことから、シーケンスが 2 つのプロパティで定義されていることがわかります。 初期要素 次の要素を生成する関数 ライブラリの使用: 2.0_ 依存関係: SeqGen ライブラリは Rust プログラミング言語で書かれています。フォローアップするには、 必要があります。 Rust をインストールする 2.1_ プロジェクトの作成: SeqGen ライブラリを使用する新しいプロジェクトを作成しましょう。貨物を使ってそれを行うことができます。 $ cargo new --bin sequence && cd sequence 次に、ライブラリを依存関係としてプロジェクトに追加しましょう。 $ cargo add seqgen これで、ライブラリを使用する準備が整いました。 シーケンスの作成: SeqGen では、シーケンス作成プロセスは、「シーケンスとは」セクションで結論付けたシーケンスの 2 つのプロパティに直接マッピングされます。初期要素と次の要素を生成する関数 (SeqGen では遷移関数と呼ばれます) を定義する必要があります。 線形シーケンスを作成しましょう。 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 }); } 型はシーケンスを表す構造体です。この型に対して関連する関数 を呼び出すと、未定義の新しいインスタンスが取得されます。この未定義のインスタンスでは、メソッドを呼び出して定義できます。 Sequence new() 最初のメソッドは、 で、要素のベクトルを引数として受け取り、それをインスタンスの初期要素として設定します。 initial_elements() 2 番目のメソッドは、 で、現在利用可能な要素から次の要素への遷移を表すクロージャを引数として受け取ります。 transition_function() このクロージャは 2 つの引数にアクセスできます。1 つ目は現在利用可能な要素を表す で、2 つ目は生成中の現在の要素のインデックスである ( 型) です。遷移関数の説明については、以下の表を参照してください。 alive_elements current_element_index usize 生成中の現在の要素 生きている要素 現在の要素インデックス 1 [0] 1 2 [0, 1] 2 3 [0, 1, 2] 3 n [0, 1, 2, 3, …, n] n は 型です。この記事の後半で 型について説明します。 alive_elements SequencePart SequencePart 要素のインデックスは線形シーケンス内の値でもあるため、上記の例を次のように単純化できます。 use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); } ここでは、初期要素を定義する必要はなく、ライブ要素にアクセスする必要もありません。インデックス (この場合は という名前) が必要なだけで、それを返すだけです。 i 同様に、フィボナッチ数列を定義できます。 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 }); } フィボナッチ数列は指数関数的に増加するため、この定義で 187 を超える要素を生成すると、 がオーバーフローします。より適切な定義では、 の代わりに big int を使用します。 u128 u128 : シーケンスの使用 シーケンスを定義したら、その要素にアクセスできます。 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}"); } ここでは、要素への不変参照 (この場合は ) を返す メソッドを使用して 111 番目の要素にアクセスしています。 &usize nth_element() を変更可能にしたことに注意してください。これは、 メソッドがシーケンスの生きている要素を変更するためです。 linear_seq nth_element() このようにして、シーケンスの任意の要素 (インデックス の要素から のインデックスの要素まで) にアクセスできます。 0 usize::MAX Rust イテレータと同様にシーケンスを反復処理することもできます。 use seqgen::prelude::*; fn main() { let linear_seq = Sequence::new().transition_function(|_, i| i); linear_seq.for_each(|e| println!("{e}")); } このコードは、シーケンスのすべての要素 (要素 から要素 まで) を出力します。 0 usize::MAX $ cargo run -q 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ... 次のコードを使用して、シーケンスから奇数の要素を取得できます。 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}")); } 出力: $ cargo run -q 1 3 5 7 9 11 13 ... 私たちが定義するシーケンスは遅延的です。つまり、必要な場合 (反復の場合)、または明示的に要求された場合 ( メソッドを使用) がない限り、要素は生成されません。 nth_element() : シーケンスの一部を操作する 場合によっては、シーケンスの一部のみを操作する必要がある場合があります。この場合はシーケンス部分があります。 シーケンス部分には次の 3 種類があります。 AliveElementsPart ImmutableRangePart MutableRangePart AliveElementsPart: シーケンス上で メソッドを使用してライブ要素を取得できます。 alive_elements() 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} "); } } このコードは、生きているすべての要素 (この場合は 111 個の要素を事前に生成したため、0 ~ 110) を出力します。 不変範囲パート: 不変範囲は、生きている要素の範囲です。配列を変異させることはできません。不変範囲を作成し、そのすべての要素が有効ではない場合は、エラー ( エラー) が発生します。 DeadRange を返す メソッドを使用して、不変の範囲を作成できます。 バリアントは で、 バリアントは です。 バリアント (範囲の始点が終点よりも大きい場合) である場合もあれば、 バリアント (範囲のすべての要素が有効ではない場合) である場合もあります。 Result range() Ok ImmutableRangePart Err RangeError RangeError InvalidRange DeadRange 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}") } } 生きている要素がないため、このコードは エラーでパニックになります。これは次のように修正できます。 DeadRange 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}") } } ここでは、範囲を有効にするために 3 つの要素を生成しました。 MutableRangePart: 変更可能な範囲では、シーケンスを変更する (要素を生成する) ことができます。 次のように可変範囲を使用できます。 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}"); } } このコードは 0 から 110 までの要素を出力します。 結論: この記事を最後まで読んでいただきありがとうございます。何かお役に立てば幸いです。このライブラリを改善できる提案がある場合は、 ください。また、 たい場合は、それは素晴らしいことです。 GitHub で問題を開いて ライブラリに貢献し でも公開されています ここ