제너레이터 개요

제너레이터 개요 (Generators overview)

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

제너레이터(Generator)는 간단한 이터레이터(iterator)를 쉽게 구현할 수 있는 방법이에요. Iterator 인터페이스를 구현하는 클래스를 직접 만들어야 하는 부담과 복잡함이 전혀 없죠.

제너레이터는 데이터를 foreach 루프에 넘겨 주는 편리한 방법이기도 해요. 미리 배열을 메모리에 통째로 만들어 두지 않아도 되거든요. 값을 전부 담은 배열을 미리 만들려면 프로그램이 메모리 한도를 넘어버리거나, 값을 생성하는 데 상당한 처리 시간이 필요할 수도 있어요. 대신 제너레이터 함수를 쓰면 돼요. 일반 함수와 거의 같지만, 한 번만 return 하는 대신, 필요한 만큼 여러 번 yield 하면서 반복할 값을 하나씩 제공할 수 있죠. 이터레이터와 마찬가지로 임의 접근(random data access)은 불가능해요.

이걸 보여 주는 간단한 예시로 range() 함수를 제너레이터로 다시 구현해 볼게요. 기본 range() 함수는 모든 값을 담은 배열을 만들어서 반환해야 하기 때문에 배열이 커질 수 있어요. 예컨대 range(0, 1000000)을 호출하면 10MB가 훌쩍 넘는 메모리를 쓰게 돼요.

대안으로 xrange() 제너레이터를 구현해 볼 수 있어요. 이건 Iterator 객체를 만들고 내부에서 제너레이터의 현재 상태만 추적하면 되기 때문에, 필요한 메모리가 1킬로바이트도 안 되는 수준이에요.

예제 #1: range()을 제너레이터로 구현하기

<?php
function xrange($start, $limit, $step = 1) {
    if ($start <= $limit) {
        if ($step <= 0) {
            throw new LogicException('Step must be positive');
        }

        for ($i = $start; $i <= $limit; $i += $step) {
            yield $i;
        }
    } else {
        if ($step >= 0) {
            throw new LogicException('Step must be negative');
        }

        for ($i = $start; $i >= $limit; $i += $step) {
            yield $i;
        }
    }
}

/*
 * Note that both range() and xrange() result in the same
 * output below.
 */

echo 'Single digit odd numbers from range():  ';
foreach (range(1, 9, 2) as $number) {
    echo "$number ";
}
echo "\n";

echo 'Single digit odd numbers from xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
    echo "$number ";
}

위 예제의 출력은 다음과 같아요.

Single digit odd numbers from range():  1 3 5 7 9
Single digit odd numbers from xrange(): 1 3 5 7 9

제너레이터 객체 (Generator objects)

제너레이터 함수를 호출하면 내부 Generator 클래스의 새 객체가 반환돼요. 이 객체는 앞으로만 진행되는(forward-only) 이터레이터 객체가 그러하듯 Iterator 인터페이스를 구현하고, 제너레이터의 상태를 조작할 수 있는 메서드들을 제공해요. 여기에는 값을 보내고(send) 다시 받아오는(return) 기능도 포함돼 있죠.