[Rust] Iterator method

suwonyoo·2025년 2월 8일

Rust Study | Special

목록 보기
4/4
post-thumbnail

정의

  • Iterator pattern(반복자 패턴) : 일련의 아이템들에 대해 순서대로 어떤 작업을 수행할 수 있도록 도와줌
  • 컬렉션(벡터, 배열, 슬라이스의 요소를 하나씩 반환하는 개념

3가지 form의 iteration

  • iter(), which iterates over &T.
  • iter_mut(), which iterates over &mut T.
  • into_iter(), which iterates over T.

itertor는 lazy하다.

  fn main() {
      let input = vec![1, 2, 3];

      let parity = input
          .iter()
          .map(|x| {
              print!("{}", x);
              x % 2
          });

      for p in parity {
          print!("{}", p);
      }
  }
  • Rust에서 이터레이터(iterator) 는 lazy(지연 평가)
  • 이터레이터를 생성해도 즉시 연산이 수행되지 않고, 값을 소비(consume)하는 메서드가 호출될 때만 동작 합니다.
112031

Iter()

  • iter()는 컬렉션을 불변 참조자로 순회할 수 있도록 하는 Iterator를 반환한다.
  • 불변 참조를 반환 -> 원본 데이터 수정 불가능
    : 메소드들도 결론적으로 원본 값을 바꾸지 못함

여기서, String과 같은 문자열의 각 문자를 순회할 수 있는 iterator를 만드는 chars는, 문자열의 개별 문자(char)을 만듦. 그래서 해당 값을 받을 때 &꼴이 아닌 그대로 받음.

collect()

  fn collect<B>(self) -> B
  where
      B: FromIterator<Self::Item>,
      Self: Sized,
  • iterator의 모든 요소를 모아서 B타입의 컬렉션을 반환하는 메서드

B: FromIterator<Self::Item> 이라는 조건 때문에, B는 반드시 이터레이터에서 FromIterator를 구현한 타입

  • iterator의 요소들을 모아서 새로운 컬렉션으로 변환
let v = vec![1, 2, 3];
let squared: Vec<i32> = v.iter().map(|x| x * x).collect();

println!("{:?}", squared); // ✅ [1, 4, 9]

유용한 Iterator method

Rust의 Iterator는 컬렉션을 순회하면서 데이터를 처리할 수 있도록 해주는 강력한 기능을 제공한다.
아래는 자주 사용하는 Iterator 메서드와 종류를 정리한 내용이다.


1. 기본 변환 (map, filter, flatten)

메서드설명
map(f)각 요소를 함수 f를 적용하여 변환
filter(f)함수 ftrue를 반환하는 요소만 남김
filter_map(f)Option<T>을 반환하는 f를 적용하고 Some 값만 남김
flatten()중첩된 Iterator를 평탄화
flat_map(f)f를 적용하고 평탄화
let v = vec![1, 2, 3, 4];
let squared: Vec<_> = v.iter().map(|x| x * x).collect();
println!("{:?}", squared); // [1, 4, 9, 16]

2. 소유권 변경 (cloned, copied)

메서드설명
cloned()&TT로 변환 (복사 가능 타입에서 사용 가능)
copied()Copy 가능한 &TT로 변환
let v = vec![1, 2, 3];
let owned: Vec<_> = v.iter().copied().collect();
println!("{:?}", owned); // [1, 2, 3]

3. 집계 및 연산 (sum, product, count)

메서드설명
sum()모든 요소를 합산
product()모든 요소를 곱셈
count()요소 개수 반환
max() / min()최댓값 / 최솟값 반환
max_by(f) / min_by(f)비교 함수 f를 사용하여 최댓값 / 최솟값 반환
fold(init, f)초기값 init에서 시작하여 f를 적용하면서 축적
reduce(f)첫 번째 요소부터 시작하여 f를 적용하면서 축적
let v = vec![1, 2, 3, 4];
let sum: i32 = v.iter().sum();
println!("{}", sum); // 10

let product: i32 = v.iter().product();
println!("{}", product); // 24

4. 검색 및 확인 (any, all, find)

메서드설명
any(f)f를 만족하는 요소가 하나라도 있으면 true 반환
all(f)모든 요소가 f를 만족하면 true 반환
find(f)f를 만족하는 첫 번째 요소를 반환
find_map(f)Option<T>을 반환하는 f를 적용하고 Some을 반환하면 종료
position(f)f를 만족하는 첫 번째 요소의 인덱스를 반환
let v = vec![1, 2, 3, 4];
let has_even = v.iter().any(|&x| x % 2 == 0);
println!("{}", has_even); // true

let all_positive = v.iter().all(|&x| x > 0);
println!("{}", all_positive); // true

5. 슬라이싱 및 제한 (take, skip, enumerate)

메서드설명
next()다음 요소를 가져오고, 상태를 한 단계 앞으로 이동
take(n)처음 n개의 요소만 반환
skip(n)처음 n개의 요소를 건너뜀
step_by(n)n 간격으로 요소를 선택
chain(other)두 개의 Iterator를 연결
enumerate()(index, value) 쌍으로 변환
zip(other)두 개의 Iterator(a, b) 쌍으로 결합
let v = vec![1, 2, 3, 4, 5];
let first_three: Vec<_> = v.iter().take(3).collect();
println!("{:?}", first_three); // [1, 2, 3]

let skipped: Vec<_> = v.iter().skip(2).collect();
println!("{:?}", skipped); // [3, 4, 5]

비효율적으로 풀었지만, leetcode#13



impl Solution {
    pub fn check_value(ss:(&char, &char), sum : i32) -> i32 {
            match ss {
                ('I', 'V') |('I', 'X' ) => sum-1,
                ('I', _) => sum+1,
                ('V', _) => sum+5,
                ('X', 'L') | ('X', 'C') => sum-10,
                ('X', _) => sum+10,
                ('L', _) => sum+50,
                ('C', 'D') | ('C', 'M')  => sum- 100,
                ('C', _) => sum+100,
                ('D', _) => sum+500,
                ('M', _) => sum+1000,
                (_, _) => sum,
            }
        }

    pub fn roman_to_int(s: String) -> i32 {
            let mut sum = 0;
            let mut last : char = ' ';

            if s.len() != 1 {
                for (ch1, ch2) in s.chars().zip(s.chars().skip(1)) {
                    sum = Solution::check_value((&ch1, &ch2), sum);
                    last = ch2
                }   
                // println!("{}", last);
                Solution::check_value((&last, &' '), sum)
            }
           else {
              Solution::check_value((s.chars().next().unwrap_or(' '), ' '), sum)
          }


    }
}

사실은, 문제 자체에서, 어긋나는 케이스는 주지 않겠다고 이야기가 나왔던 상태여서, match자체를 위처럼 아주 이상하게 짤 필요는 없었지만, iterator method를 공부한 이유가 결국 이 문제를 풀다가 나왔으므로, 원래 풀이를 그냥 놓고 분석

s.chars().zip(s.chars().skip(1))

  • 의도: 두 요소씩 뽑아 내고 싶었음, match자체를 두 튜플을 매개변수로 받았기 때문
  • 방법: s.chars().skip(1)로 한칸 미뤄진 iterator를만든 후, 이를 zip을 이용하여 쌍으로 결합함. 그 후, 이를 하나씩 뽑아내면 인덱스 기준 (1,2), (2,3), (3,4)... 꼴의 튜플을 뽑아낼 수 있다고 생각. 이 때 char 그대로 반환하기 떄문에 참조자를 안붙여야함
	for (ch1, ch2) in s.chars().zip(s.chars().skip(1))
  • 출력값
	Zip { a: Chars(['H', 'e', 'l', 'l', 'o']), b: Skip { iter: Chars(['H', 'e', 'l', 'l', 'o']), n: 1 } }
  • 하지만 문제 발생 -> 문자열 길이가 1이면? 답이 없음, 그래서 따로 조건을 두고 분기하여 진행함(s.len()=1).

Solution::check_value((s.chars().next().unwrap_or(' '), ' '), sum)

  • s.chars().next()의 출력값은 Option<T> 데이터형이므로, unwrap으로 원값 뽑아낼 수 있음
	Solution::check_value((s.chars().next().unwrap_or(' '), ' '), sum)

0개의 댓글