yield naredba u C#


yield je ključna riječ (naredba) koja se pojavila izlaskom C# 2.0 i Visual Studio 2005. Oficijelna (MSDN) definicija kaže da je ovo ključna riječ koja upućuje informaciju kompajleru da metoda u kojoj se pojavljuje yield posjeduje dio koda za iteraciju. Kada kompajler naiđe na ovu riječ on formira klasu u koju implementira logiku i način na koji će se yield izvršavati. Ovo znači da yield nije osobina .NET Frameworka, nego čisto C# programskog jezika. U bloku za iteraciju yield se koristi zajedno sa return za vraćanje vrijednosti u objekat enumeracije. yield se može koristiti i sa break da bi se prekinula iteracija. yield koristi tzv. lazy evaluaciju, da se evaluacija odigrava u vrijeme izvršavanja ovog koda. Pogledajmo sljedeći dio koda:

static IEnumerable<int> F()
{  return 1;
   return 2; // Nikad se neće izvršiti
}

U ovom kodu imamo naredbu return jednu iza druge. Naravno druga naredba neće se nikad izvršiti, odnosno metoda F() uvijek vraća vrijednost 1. Pogledajmo gornji kod kada koristimo yield:

static IEnumerable<int> F()
{
  yield return 1;
  yield return 2; // Može biti izvršeno
}

Za razliku od prethodnog primjera, yield zajedno sa return naredbom će vratiti vrijednost 2 kada se ova metoda poziva kroz foreach petlju.

static IEnumerable<int> F()
foreach(var item in F())
 Console.WriteLine(item.ToString());

Kada izvršimo ovaj dio koda izlaz na konzoli je sljedeći:

Izlaz:
1
2

Potrebno je imati na umu određena ograničenja prilikom korištenja ove naredbe

  • Ako yield koristimo izvan bloka metoda, operatora kompajler će prijaviti grešku
  • yield se ne može koristiti unutar anonimne funkcije
  • yield ne smijemo također koristiti u bloku finally
  • yield return ne smijemo koristiti kada try posjeduje catch u bilo kojoj varijanti

Primjena yield naredbe u rješavanju problema

Sa yield naredbom vrlo elegantno možemo rješavati određene probleme pogotovi kada je potrebno da se određenom iteracijom dobije rezultat. Jedan od problema koji se vrlo jednostavno rješava je prebrojavanje prostih brojeva.

Prebrojavanje prostih brojeva

U jednom od postova implementirana je metoda za provjeravanje prostih brojeva, bez korištenja yield naredbe, kada se govorilo od ParallelFx. U narednom tekstu data je implementacija prebrojavanja prostih brojeva manji od n uz upotrebu yield naredbe.

//Prebrojavanje prostih brojeva manji od n
public static IEnumerable<int> VratiProsteBrojeve (int n)
{
  yield return 2;
  yield return 3;
  for (int i = 4; i < n; i++)
  {
   bool bprostBroj = true;
   for (int j = 2; j*j <= i; j++)
    if (i % j == 0)
     bprostBroj = false;
  if(bprostBroj)
   yield return i;
  }
static void Main(string[] args)
{
   //Prebrojavanje prostih brojeva manji od n
   foreach (var item in VratiProsteBrojeve (10))
     Console.WriteLine(item.ToString());
   //Press any key to continue...
   Console.Read();
}

Obzirom da posjedujemo metodu za prebrojavanje prostih brojeva otvaraju nam se bezbroj mogućnosti da kroz LINQ također vršimo određene upite. Npr. Želimo da izračunamo sumu prostih brojeva manjih od n. Ništa lakše:

var suma = Program. VratiProsteBrojeve (n).Sum();

Sada naš izlaz na konzolu izgleda kao na sljedećoj slici.

Ako želimo da izračunamo sumu kvadrata prostih brojeva manji od n imamo sljedeći primjer:

var sumaKvadrata = (from p in VratiProsteBrojeve (n) select p * p).Sum();

Rastavljanje na proste faktore

Svi smo učili iz matematike u Osnovnoj školi rastavljanje broja na proste faktore. U ovom primjeru ćemo vidjeti na koji način koristimo yield naredbu prilikom rješavanja problema koji zahtjevaju korištenje rekurzije. Uradimo prvo pješice jedan primjer čisto da se podsjetimo šta to znači. Npr. rastavi na proste faktore broj 10.10=2*5, jer su 2 i 5 prosti brojevi.Potrebno je sada implementirati algoritam za rastavljanje broja na proste faktore. U narednom primjeru prikazana je metoda za foktoriziranje broja:

//Rastavljanje broja n na proste faktore n=n1*n2*n3*...nn; ni-prosti brojevi
public static IEnumerable<string> Foktoriziraj(string izraz, int n)
{
   //Ako je 1 vrati izraz
   if (n == 1)
     yield return izraz;
   else
    {
     //Prolazimo sve brojeve manje od n
     for (int i = 2; i <= n; i++)
       {
         //Ako je n djeljiv sa i
         if ((n % i) == 0)
          {
            int q = n / i;
            //Zapišimo u izraz broj i
            if (!izraz.EndsWith("= ")) izraz += " * ";
               izraz += i.ToString();
            //Sada q postaje n i primjenjujemo isti postupak sve dok ne postane 1
             foreach (string podizraz in Foktoriziraj(izraz, q))
               yield return podizraz;
             yield break;
           }
        }
     }
}

Ako želimo da broj 100 rastavimo na proste faktore pozovimo gornju metodu u sljedećoj formi.

Console.WriteLine(Foktoriziraj("100 = ", 100).SingleOrDefault());

yieldoutput001

U koliko želimo rastaviti na proste faktore prvih 10 prirodnih brojeva potrebno je:

for (int i = 1; i < 10; i++ )
   Console.WriteLine(Foktoriziraj(i.ToString() + " = ", i).SingleOrDefault());

Rezultat je dan na sljedećoj slici:

yieldoutput002

Reference:

  1. www.msdn.com
  2. The C# Programming Language 3th edition, Anders Hejlsberg
  3. http://startbigthinksmall.wordpress.com/2008/06/09/behind-the-scenes-of-the-c-yield-keyword/
  4. http://www.codeproject.com/KB/cs/IEnumerableAndYield.aspx

About Bahrudin Hrnjica

PhD in Mechanical Engineering, Microsoft MVP for .NET. Likes .NET, Math, Mechanical Engineering, Evolutionary Algorithms, Blogging.

Posted on 03/09/2009, in .NET, C# and tagged , . Bookmark the permalink. 3 Comments.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s