Glowhollow - Fr 25.01.19 18:30
Titel: primfaktorZerlegung Verständnisfrage (speziell Yield)
Hallo,
ich bin im Netz über folgenden Code gestolpert, den ich auch gleich mitkommentiere, damit ihr sehen könnt, was ich verstanden habe, und was nicht.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46:
| class Primes { static IEnumerable<int> DividerSequence() { yield return 2; yield return 3; for(int i = 6; true; i+=6){ yield return i - 1; yield return i + 1; } }
public static ICollection<int> GetPrimeFactors(int number) { if (number <= 0) { throw new ArgumentException("Positive integer expected.", "number"); }
LinkedList<int> primeFactors = new LinkedList<int>(); foreach (int factor in DividerSequence()) { while (number % factor == 0) { primeFactors.AddLast(factor); number /= factor; } if (factor * factor > number) { break; } } if (number > 1) { primeFactors.AddLast(number); } return primeFactors; } }
class Program{ static void Main(string[] args) { for (int number = 1; number < 100; number++) { ICollection<int> primeFactors = Primes.GetPrimeFactors(number); Console.WriteLine( "n={0}, Primes=[{1}]", number, string.Join(", ", primeFactors.Select(x => x.ToString()).ToArray())); } } } |
speziell verstehe ich Yield nicht so ganz, auch die Dokumentation von Microsoft, ist so mit fachwörten gespickt, das ich mir schwer tue es zu verstehen.
Ich kann natürlich das ganze jetzt im debugger laufen lassen und gucken, was er da genau macht, aber ich möchte ja verstehen, was hier passiert.
Kann mich hier jemand n bischen erleuchten ?
Th69 - Fr 25.01.19 19:07
Ein Methode, mit dem Schlüsselwort
yield return innerhalb, wird vom Compiler intern in eine State-Machine übersetzt, so daß bei jedem Aufruf der Methode der nachfolgende Code (nach dem
yield return) weiter ausgeführt wird. Wichtig dabei ist, daß der Rückgabetyp
IEnumerable<T> ist, so daß eine Folge generiert wird.
Bei statischen Werten würde man z.B. eine List<T> zurückgeben:
C#-Quelltext
1: 2: 3: 4:
| List<int> Test() { return new List<int> { 2, 3, 5, 7, 11, 13, 17, 19 }; } |
Bei der Methode
DividerSequence() wird quasi eine unendliche Liste erzeugt, aber eben mittels "Lazy Evaluation" ausgewertet, d.h. erst wenn wirklich auf die generierte Enumeration zugegriffen wird.
Der Code
C#-Quelltext
1: 2:
| foreach (int x in DividerSequence()) Console.WriteLine(x); |
erzeugt also diese unendliche Liste von Primfaktorkandidaten.
Edit: Durch
yield break oder wenn das Ende der Methode erreicht ist, wird kein weiteres Element der Folge mehr generiert - was meistens benutzt wird, um endliche Folgen (Auflistungen) zu generieren.
Etwas ausführlicher wird es noch unter
Essential .NET: Benutzerdefinierte Iteratoren mit „yield“ [
https://msdn.microsoft.com/de-de/magazine/mt809121.aspx] sowie
Yield return in C# [
https://www.kenneth-truyers.net/2016/05/12/yield-return-in-c/] (auf englisch) erklärt.
PS: Die Schleife mit
+=6 wird genommen, da dies eine Optimierung gegenüber
+=2 (nur ungerade Zahlen ist), da ja alle durch 3 teilbaren ungeraden Zahlen (9, 15, 21, ...) keine Primzahlen sein können.