제너레이터와 이터레이터
Coddy PHP 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 91개 중 62번째.
제너레이터(Generators)는 전체 Iterator 인터페이스를 구현하지 않고도 이터레이터를 생성하는 간단한 방법을 제공합니다. 제너레이터 함수는 return 대신 yield를 사용하여 값을 한 번에 하나씩 생성하며, 각 값 사이에서 실행을 일시 중지합니다.
다음은 일련의 숫자를 생성하는 기본적인 제너레이터입니다:
<?php
function countTo(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(3) as $number) {
echo "$number\n";
}
출력:
1
2
3주요 장점은 메모리 효율성입니다. 메모리에 전체 배열을 생성하는 대신, 제너레이터는 필요할 때마다 값을 생성합니다. 이는 대규모 데이터 세트를 작업할 때 매우 중요합니다. 백만 개의 레코드를 처리하는 제너레이터는 10개를 처리하는 제너레이터와 동일한 메모리를 사용합니다.
제너레이터는 키-값 쌍을 생성(yield)할 수도 있습니다:
<?php
function userGenerator(): Generator {
yield 'admin' => 'Alice';
yield 'editor' => 'Bob';
}
foreach (userGenerator() as $role => $name) {
echo "$role: $name\n";
}
출력:
admin: Alice
editor: Bob자신만의 클래스를 foreach로 반복 가능하게 만들려면 Iterator 인터페이스를 구현하세요. 하지만 대부분의 경우, 제너레이터(generator)가 상용구 코드(boilerplate code)가 적은 더 깔끔한 해결책을 제공합니다. 이는 클래스가 모든 항목을 한 번에 메모리에 로드하지 않고도 항목 컬렉션을 노출해야 할 때 OOP에서 특히 유용합니다.
챌린지
쉬움대규모 컬렉션을 한꺼번에 메모리에 로드하지 않고 효율적으로 순회하기 위해 제너레이터(generators)를 사용하는 제품 인벤토리 시스템을 구축해 보겠습니다. 제품을 저장하고 다양한 방식으로 순회할 수 있는 제너레이터 메서드를 제공하는 클래스를 만들 것입니다.
코드는 세 개의 파일로 구성됩니다:
Product.php— 인벤토리의 항목을 나타내는Product클래스를 생성합니다. 생성자 프로모션(constructor promotion)을 사용하여 public$sku(string), public$name(string), 그리고 public$price(float)를 정의합니다.Inventory.php— 제품 컬렉션을 관리하고 이를 순회하는 제너레이터 메서드를 제공하는Inventory클래스를 생성합니다. Product 파일을 포함해야 합니다. 이 클래스는 다음을 수행해야 합니다:- 제품을 저장할 private 배열 속성을 가집니다.
- 컬렉션에 제품을 추가하는
addProduct(Product $product)메서드를 가집니다. - 각 제품을 하나씩 생성(yield)하는 제너레이터 메서드
allProducts(): Generator를 가집니다. - 주어진 최소 가격보다 크거나 같은 가격의 제품만 생성하는 제너레이터 메서드
expensiveProducts(float $minPrice): Generator를 가집니다. 이때 키는 SKU이고 값은 제품인 키-값 쌍을 생성해야 합니다.
main.php— Inventory 파일을 포함합니다. 하나의 입력값으로 최소 가격 임계값을 받게 됩니다.Inventory인스턴스를 생성하고 다음 세 가지 제품을 추가합니다:- SKU:
"A001", Name:"Laptop", Price:999.99 - SKU:
"A002", Name:"Mouse", Price:29.99 - SKU:
"A003", Name:"Monitor", Price:349.99
먼저,
allProducts()를 순회하며 각 제품의 이름을 개별 줄에 출력합니다.그런 다음 빈 줄을 하나 출력하고, 입력받은 임계값을 사용하여
expensiveProducts()를 순회합니다. 각 고가 제품에 대해"[sku]: [name] - $[price]"형식으로 개별 줄에 출력합니다. 가격은 소수점 아래 2자리까지 정확히 포맷팅하여 출력하세요.- SKU:
이 챌린지는 제너레이터가 컬렉션을 지연(lazily) 순회하게 해주는 방법을 보여줍니다. 각 제품은 요청될 때만 생성되므로, 대규모 인벤토리에 대해 메모리 효율적인 접근 방식이 됩니다. expensiveProducts()에서의 키-값 생성은 제너레이터가 어떻게 구조화된 순회 데이터를 제공할 수 있는지 보여줍니다.
치트 시트
제너레이터는 return 대신 yield를 사용하여 이터레이터를 생성하는 메모리 효율적인 방법을 제공합니다. 제너레이터는 값을 한 번에 하나씩 생성하며, 각 값 사이에서 실행을 일시 중지합니다.
기본적인 제너레이터 함수:
<?php
function countTo(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(3) as $number) {
echo "$number\n";
}
제너레이터는 키-값 쌍을 생성할 수 있습니다:
<?php
function userGenerator(): Generator {
yield 'admin' => 'Alice';
yield 'editor' => 'Bob';
}
foreach (userGenerator() as $role => $name) {
echo "$role: $name\n";
}
주요 장점은 메모리 효율성입니다. 제너레이터는 메모리에 전체 배열을 빌드하는 대신 필요에 따라 값을 생성합니다. 이는 대규모 데이터 세트나 OOP 컨텍스트의 컬렉션을 작업할 때 특히 유용합니다.
직접 해보기
<?php
require_once 'Inventory.php';
// 입력 읽기
$minPrice = floatval(trim(fgets(STDIN)));
// TODO: Inventory 인스턴스 생성
// TODO: 세 개의 제품 추가:
// SKU: "A001", Name: "Laptop", Price: 999.99
// SKU: "A002", Name: "Mouse", Price: 29.99
// SKU: "A003", Name: "Monitor", Price: 349.99
// TODO: allProducts()를 순회하며 각 제품의 이름(name)을 출력
// TODO: 빈 줄 출력
// TODO: $minPrice를 사용하여 expensiveProducts()를 순회
// 각 비싼 제품에 대해 "[sku]: [name] - $[price]" 형식으로 출력
// 가격은 소수점 둘째 자리까지 표시
?>이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8매직 메서드
매직 메서드 소개__toString 및 __debugInfo__get, __set, __isset, __unset__call 및 __callStatic__clone 및 객체 복제__serialize 및 __unserialize요약 - 커스텀 컬렉션