시퀀스를 생성할 수 있는 라이브러리를 인터넷에서 검색하면 거의 찾을 수 없습니다. 하지만 시퀀스는 이산수학과 컴퓨터 과학의 핵심 개념입니다.
이 짧은 기사에서는 SeqGen 이라는 시퀀스 생성을 위해 제가 작성한 라이브러리를 살펴보겠습니다.
시퀀스(비공식적으로 말하면)는 각 요소의 생성이 이전 요소를 기반으로 하는 요소 집합(주로 숫자)입니다.
가장 기본적인 예는 첫 번째 요소가 0이고 다음 요소가 이전 요소에 1을 더한 양의 정수의 간단한 선형 시퀀스이므로 첫 번째 요소에 1을 더하여 두 번째 요소를 얻을 수 있고 세 번째 요소를 얻을 수 있습니다. 두 번째 요소에 1을 추가하는 방식으로 요소를 구성합니다. 선형 시퀀스는 다음과 같습니다: {0, 1, 2, 3, …, n}
.
더 복잡한 예는 처음 두 요소가 0과 1이고 다음 요소가 이전 두 요소의 합인 피보나치 수열일 수 있습니다. 피보나치 수열은 다음과 같습니다: {0, 1, 1, 2, 3, 5, 8, 13, 21, …, n}
위에서 시퀀스는 두 가지 속성으로 정의되어 있음을 알 수 있습니다.
2.0_
종속성:SeqGen 라이브러리는 Rust 프로그래밍 언어로 작성되었습니다. 후속 조치를 취하려면 Rust가 설치되어 있어야 합니다.
2.1_
프로젝트 생성:SeqGen 라이브러리를 사용하기 위해 새 프로젝트를 만들어 보겠습니다. 화물로 그렇게 할 수 있습니다:
$ cargo new --bin sequence && cd sequence
이제 프로젝트에 종속성으로 라이브러리를 추가해 보겠습니다.
$ cargo add seqgen
이제 라이브러리를 사용할 준비가 되었습니다.
SeqGen에서 시퀀스 생성 프로세스는 시퀀스란 무엇입니까 섹션에서 결론을 내린 시퀀스의 두 가지 속성에 직접 매핑됩니다. 초기 요소와 다음 요소를 생성하는 함수(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()
입니다.
두 번째 메소드는 현재 사용 가능한 요소에서 다음 요소로의 전환을 나타내는 클로저를 인수로 사용하는 transition_function()
입니다.
이 클로저는 두 개의 인수에 액세스할 수 있습니다. 첫 번째는 현재 사용 가능한 요소를 나타내는 alive_elements
이고, 두 번째는 생성 중인 현재 요소의 인덱스인 current_element_index
( usize
유형)입니다. 전환 기능에 대한 설명은 아래 표를 참조하세요.
현재 세대 요소 | 살아있는_요소 | 현재_요소_색인 |
---|---|---|
| | |
| | |
| | |
| | |
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개 이상의 요소를 생성하면 u128
오버플로됩니다. 더 나은 정의는 u128
대신 big int를 사용하는 것입니다.
시퀀스를 정의한 후에는 해당 요소에 액세스할 수 있습니다.
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}"); }
여기서는 요소에 대한 불변 참조(이 경우 &usize
)를 반환하는 nth_element()
메서드를 사용하여 111번째 요소에 액세스하고 있습니다.
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()
메서드 사용) 요소가 생성되지 않는다는 의미입니다.
때로는 시퀀스의 일부로만 작업해야 하는 경우도 있습니다. 이 경우에는 시퀀스 부분이 있습니다.
시퀀스 부분에는 세 가지 유형이 있습니다.
AliveElementsPart
ImmutableRangePart
MutableRangePart
AliveElements부분:
시퀀스에서 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개의 요소를 생성했습니다.
가변 범위 부분:
변경 가능한 범위는 시퀀스를 변경(요소 생성)할 수 있습니다.
다음과 같이 변경 가능한 범위를 사용할 수 있습니다.
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에서 문제를 공개하고 , 라이브러리에 기여하고 싶다면 정말 좋을 것입니다.
여기에도 게시됨