Tipikus interjú kérdés: yield

Becsült olvasási idő:
5 perc

Mennyi mindent használunk, de igazából lehet, hogy fogalmunk sincs róla, hogy mire is jó pontosan, és mikor is érdemes használni. A miértről nem is beszélve…

Mit mesél erről a Gemini?

A .NET-ben (C#) a yield kulcsszó pontosan ugyanazt a célt szolgálja, mint a Pythonban: IEnumerable<T> vagy IAsyncEnumerable<T> típusú adatfolyamokat hozhatsz vele létre úgy, hogy a kód lusta kiértékeléssel (lazy evaluation), elemenként adja vissza az eredményeket.

C#-ban két formája létezik:

  • yield return <érték>: Visszaad egy elemet, majd felfüggeszti a metódus futását a következő elem kéréséig.
  • yield break: Azonnal leállítja az iterációt (megszakítja a generálást).

Mire jó C#-ban?

1. Memóriatakarékosság nagy adathalmazoknál

Ha nem List<T>-et adsz vissza, nem kell a teljes gyűjteményt lefoglalni a RAM-ban.

C#

using System;
using System.Collections.Generic;

// PÉLDA 1: Hagyományos megközelítés (Memóriatakaréktalan)
public List<int> GetNumbersList()
{
    var list = new List<int>();
    for (int i = 0; i < 1_000_000; i++)
    {
        list.Add(i); // Mind az 1 milló elem belekerül a RAM-ba
    }
    return list;
}

// PÉLDA 2: yield használata (Memóriatakarékos)
public IEnumerable<int> GetNumbersYield()
{
    for (int i = 0; i < 1_000_000; i++)
    {
        yield return i; // Mindig csak az aktuális számot generálja le
    }
}

2. Szűrés és adatfeldolgozás yield break-kel

A yield break használatával feltételhez kötheted az adatfolyam végét anélkül, hogy bonyolult feltételkezelést kellene írnod a ciklus köré:

C#

public IEnumerable<string> GetActiveUsers(List<string> users)
{
    foreach (var user in users)
    {
        if (user == "ADMIN_STOP")
        {
            yield break; // Azonnal leállítja az elemek kiadását
        }

        if (!string.IsNullOrEmpty(user))
        {
            yield return user;
        }
    }
}

3. Óriási fájlok feldolgozása soronként

Nem kell a gigabájtos fájlokat egyszerre a memóriába tölteni (File.ReadAllLines helyett):

C#

using System.IO;
using System.Collections.Generic;

public IEnumerable<string> ReadLargeFile(string filePath)
{
    using (StreamReader reader = new StreamReader(filePath))
    {
        string? line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line; // Mindig csak egyetlen sort tart a memóriában
        }
    }
}

// Használat:
foreach (string line in ReadLargeFile("huge_log.txt"))
{
    if (line.Contains("ERROR"))
    {
        Console.WriteLine(line);
    }
}

4. Aszinkron adatfolyamok (C# 8.0+: IAsyncEnumerable<T>)

Modern .NET-ben hálózati kéréseknél vagy adatbázis-lekérdezéseknél aszinkron módon is használhatod:

C#

public async IAsyncEnumerable<int> FetchDataAsync()
{
    for (int i = 1; i <= 5; i++)
    {
        await Task.Delay(1000); // Pl. hálózati várakozás szimulálása
        yield return i;
    }
}

// Használat:
await foreach (var item in FetchDataAsync())
{
    Console.WriteLine(item); // Másodpercenként jelenik meg egy elem
}

Hagyományos visszatérés vs. yield C#-ban

SzempontList<T> / Tömbyield return (IEnumerable<T>)
Kiértékelés idejeAzonnali (A metódus hívásakor lefut a teljes ciklus)Lusta / Halasztott (Csak akkor fut, amikor a foreach kéri a következő elemet)
MemóriahasználatMagas ($O(N)$ méretű lista a RAM-ban)Minimális ($O(1)$ állandó memóriagény)
Sebesség (első elem)Lassú (meg kell várni a teljes lista felépítését)Azonnali (az első elem kérésére rögtön visszatér)

Gondoltam, megkérdezem ugyanezt Typescript esetén.

Igen, pontosan ugyanígy működik TypeScriptben és JavaScriptben is.

A TypeScript a JavaScript generátor függvényeit (Generator Functions) használja, amelyeket az function* (csillaggal jelölt) szintaxisról lehet felismerni.

A működési elv teljesen megegyezik a Python és a C# esetében látottakkal: a yield felfüggeszti a függvény futását, megőrzi a belső állapotot, és lusta kiértékeléssel (lazy evaluation) adja vissza az elemeket egy Generator objektumon keresztül.

Alapvető példa TypeScriptben

A generátor függvény visszatérési típusa Generator<T, TNext TReturn,>, ahol a T a yield-elt értékek típusa.

TypeScript

// A csillag (function*) jelzi, hogy ez egy generátor függvény
function* szamGeneraltor(): Generator<number> {
  yield 1;
  yield 2;
  yield 3;
}

// Használat for...of ciklussal:
for (const szam of szamGeneraltor()) {
  console.log(szam); // Kiírja egymás után: 1, 2, 3
}

Mire jó TypeScriptben?

1. Memóriatakarékos és végtelen sorozatok

Nem kell előre tömbbe gyűjteni az elemeket:

TypeScript

function* vegtelenAzonositoGeneral(): Generator<string> {
  let id = 1;
  while (true) {
    yield `ID_${id++}`;
  }
}

const idGen = vegtelenAzonositoGeneral();

console.log(idGen.next().value); // "ID_1"
console.log(idGen.next().value); // "ID_2"
console.log(idGen.next().value); // "ID_3"

2. Adatfolyamok delegálása (yield*)

A TypeScriptben létezik a yield* utasítás is, amellyel átadhatod a vezérlést egy másik generátornak vagy tömbnek (bejárható objektumnak):

TypeScript

function* elsoResz(): Generator<number> {
  yield 1;
  yield 2;
}

function* teljesSorozat(): Generator<number> {
  yield* elsoResz(); // Delegálja az elemeket az elsoResz-ből
  yield* [3, 4, 5];  // Tömb elemeit is ki tudja adni egymás után
}

console.log([...teljesSorozat()]); // [1, 2, 3, 4, 5]

3. Aszinkron generátorok (yield + async)

Ahogy C#-ban az IAsyncEnumerable, TypeScriptben és JS-ben is létezik az AsyncGenerator (async function*). Ez hálózati adatok, lapozott API-k vagy adatfolyamok (stream) feldolgozásánál nagyon hasznos:

TypeScript

async function* adatokLetolteseAPIrol(): AsyncGenerator<string> {
  const oldalak = [1, 2, 3];
  
  for (const oldal of oldalak) {
    // Szimulált hálózati kérés (API hívás)
    const valasz = await fetch(`https://api.example.com/data?page=${oldal}`);
    const adat = await valasz.text();
    
    yield adat; // Mindig csak az aktuális oldal adatait adja vissza
  }
}

// Használat for await...of ciklussal:
async function feldolgozas() {
  for await (const oldalAdat of adatokLetolteseAPIrol()) {
    console.log("Megérkezett egy oldal:", oldalAdat);
  }
}

Összehasonlítás

NyelvFüggvény jelöléseKulcsszóBejárási felület
C#IEnumerable<T> Get()yield returnforeach / LINQ
Pythondef get():yieldfor item in gen
TypeScriptfunction* get(): Generator<T>yieldfor...of / next()
Szerző:
Kategória:
,
Bejegyzés dátuma:

Hozzászólások

Vélemény, hozzászólás?