C# Lambda Ausdrücke endlich verstehen

Magic1416

Lieutenant
Registriert
Dez. 2003
Beiträge
530
Hi,
wie der Titel schon verrät, will das Thema Lambda Expressions nicht in mein Kopf. Ich habe die MSDN gelesen sowie einige Beispiele. Ich nutze sie zum Teil auch, meistens jedoch Copy und Paste und Frage mich jedes mal, wieso sie funktionieren. Ich suche dann wieder in Google nach Erklärungen und Beispielen um sie endlich zu verstehen. Hier ein paar Beispiele:

Code:
private void dosort(cVariables variables)
        {
            Dictionary<string,cVariable> dic =  variables.ToDictionary();

            var items = from pair in dic  
                        orderby pair.Value.SequenceNumber ascending
                        select pair;

 //Das ist die Zeile. Eine Erweiterungsfunktion           
dic = items.ToDictionary(r => r.Key, r => r.Value);
  
        }

Allein die Beschreibung die Intelisense ausgeben verstehe ich nicht. Nur Google hat geholfen, wie die Argumente zu setzen sind. Ich weiß, dass in der Theorie links vom Operator der Input Parameter steht und rechts die Funktion. Satt r könnte man auch a schreiben usw. Aber warum könnte man nicht theoretisch auch als Argument folgendes einsetzen.

Code:
dic = items.ToDictionary(((KeyValuePair<string,cVariable>)items).Key,((KeyValuePair<string,cVariable>)items).Value);

Der Compiler bringt dann:

Fehler 1 Die Typargumente der System.Linq.Enumerable.ToDictionary<TSource,TKey>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,TKey>, System.Collections.Generic.IEqualityComparer<TKey>)-Methode können nicht per Rückschluss aus der Syntax abgeleitet werden. Geben Sie die Typargumente explizit an. Z:\Visual Studio Projects\Test\Test\Configuration\cVariables.cs 152 19 Test

vom Input müsste das doch gleich sein.

Weiter geht es mit folgenden Beispiel auf der MSDN Seite:
http://msdn.microsoft.com/de-de/library/bb397687.aspx

Code:
(int x, string s) => s.Length > x;

Dieses Beispiel geht überhaupt nicht.
Fehler 4 Nur assignment-, call-, increment-, decrement- und "new object"-Ausdrücke können als Anweisung verwendet werden.

Kann jemand versuchen, es mir zu erklären ?
Danke für jeden Versuch.

Gruß Magic
 
PHP:
private delegate bool del(string s, int x);

        private static void Main(string[] args)
        {
            del isLarger = (string s, int x) => s.Length > x;

            string bla = "hallo";
            int foo = 3;

            if (isLarger(bla, foo))
            {
                Console.WriteLine("{0} has more chars than {1}", bla, foo);
            }

            Console.ReadKey();
        }

so würde das funktionieren, nur bringt dieses Beispiel nicht viel :D
Aber wie genau Lambdas funktionieren und was sie überhaupt sind habe ich auch nie verstanden.

Damit kann man "anonyme" Funktionen machen, das einzige was mir dabei einfällt was das bringen könnte wäre:
1. Für etwas kleines was man öfters hintereinander benötigt keine neue Methode erstellen (übersichtlicher)
2. Man kann per Reflection nicht so einfach an diese "anonyme Methode" rankommen, was man davon hätte weiß ich jedoch nicht.
 
Zuletzt bearbeitet:
Lambdas sind immer dann sinnvoll, wenn eine Funktion genau einmal benötigt wird.

Jetzt könnte man sich natürlich fragen wo der Sinn einer Funktion liegen soll, die nur ein einziges mal verwendet wird.
Ein gutes Beispiel dafür sind standartisierte Sortierfunktionen. Für diese muss man oftmals einen Komparator angeben, der bestimmt wann ein Wert größer als ein anderer angesehen wird. Dieser Komparator ist in der Regel eine Funktion. Benötigt man diesen Komparator außerhalb der Sort Funktion noch einmal? Eigentlich nicht. Eine normale Funktion würde daher nur den Source-Code "zumüllen".

Lambdafunktionen sind außerdem auch essenziell für funktionale Programmierung, einfache Beispiele sind hier fold, map oder foreach funktionen. (Bei einer sort Funktion handelt es sich strenggenommen auch um eine Form der funktionalen Programmierung)

Hier mal ein total sinnloses Beispiel von Lamba Funktionen in C++. Die Methode "repeat" kann die mitgeführte Funktion n mal ausführen. Interessanter wird es da schon bei der "foreach" Methode, die für jedes Element des Vektors eine Funktion aufrufen kann.

Code:
#include <iostream>
#include <vector>
#include <string>

using namespace std;

inline void repeat(size_t n, void(*func)(void)) {
	for (size_t i = 0; i < n; i++)
	{
		func();
	}
}

template<typename T>
inline void forEach(const vector<T> &v, void(*func)(const T&)) {
	for (const T &element : v) {
		func(element);
	}
}

template<typename T>
inline T fold(const T &init, const vector<T> &v, T(*func)(const T&, const T&)) {
	T result(init);

	for (const T &element : v) {
		result = func(result, element);
	}

	return result;
}


int main() {
	//gibt hello world 10x aus.
	repeat(10, [] {cout << "Hello world!" << endl; });

	vector<int> someIntegers{ 1, 2, 3, 4, 5, 6 };
	//printet alle elemente des vectors in einer zeile.
	forEach<int>(someIntegers, [](const int &element) {cout << element << endl; });

	//produkt aller vektorelemente in einer zeile berechnen
	int product = fold<int>(1, someIntegers, [](const int &e1, const int &e2) {return e1 * e2; });
	cout << product << endl;

	vector<string> someStrings{ "hello", "world", "from", "fold" };
	//alle strings des vektors zusammenfügen, per leerzeichen seperiert :)
	string sentence = fold<string>("-->", someStrings, [](const string &e1, const string &e2) {return e1 + " " + e2; });
	cout << sentence << endl;
	

	system("pause");
	return 0;
}

Output:

Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
Hello world!
1
2
3
4
5
6
720
--> hello world from fold
 
Zuletzt bearbeitet:
Erstmal zu dem letzten Beispiel:
Der Compiler sagt, dass dein Ausdruck keine gültige Anweisung ist. Der Lambda-Audruck wird zu einem Wert ausgewertet. Wenn du in einer Codezeile z.B. "5;" stehen hast, wirft der Compiler eine ähnliche Fehlermeldung. Genauso ist es auch hier. Richtig wäre also:
Code:
var y = (int x, string s) => s.Length > x;

Zum ersten Beispiel:
An deiner Stelle würde ich die Doku zu der "toDictionary"-Methode genauer anschauen:
Klick

Aber auch hier:
Klick

Dir muss klar sein, wie die Methode arbeiten, was die als Parameter bekommt und was die zurückgibt:

Creates a Dictionary<TKey, TValue> from an IEnumerable<T> according to specified key selector and element selector functions.

Die Methode liefert also für ein Enumerable ein Dictionary und als Parameter bekommt sie zwei Funktionen! Genauer gesagt, übergibst du der Funktion zwei Objekte, die jeweils eine Funktion repräsentieren -> in diesem Fall Lambda-Ausdrücke.

Nun wieso und wann Lambda-Ausdrücke. Es gibt einige Fälle, wo Lambdas sinnvoll sind. Natürlich kann man auch ohne Lambdas programmieren (siehe Java < v.6 :)) Effektiver Einsatz geht durch die Übung.
Wieso Lambdas in deinem Fall:
In deinem Beispiel fungieren Lambdas als Selektoren. D.h du hast ein Objekt und wählst nur eine für dich interessante Eigenschaft heraus (Key und Value). Manchmal ist es sehr praktisch. Des Weiteren hast du als Source eine Enumerable, was im Grunde eine Liste ist (den Unterschied bitte selber herausfinden). Du hast also mehrere Elemente, die in dein Dictionary eingefügt werden sollen. Du kannst mit einem Key und einem Value als Eingabeparameter nichts anfangen, denn du brauchst Key und Value für JEDES Element deiner Enum.
Es wird wohl so funktionieren, dass die "toDictionary"-Methode jedes Element der Enumerable iteriert und für jedes Element die Selector-Funktionen ausführt. D.h. Von jedem Objekt wird Key und Value ausgewählt und in Dictionary gepackt.

Natürlich könntest du deine Enumerable selbst iterieren, sich jedes Element anschauen und passende Felder in eine Dictionary packen. Aber du willst ja Lambda-Ausdrücke üben :)
 
Ok, ich versuche zu verstehen. Hier kurz der komplette Code-Teil der Sortier-Funktion:

Code:
public void Sort()
        {
            
            sort2(this);

             
        }

        private void sort2(cVariables variables)
        {
            foreach (cVariable variable in variables)
                sort2(variable.Variables);
    
            dosort(variables); 
        }

        
        private void dosort(cVariables variables)
        {
            
            
            Dictionary<string,cVariable> dic =  variables.ToDictionary();

            var items = from pair in dic  
                        orderby pair.Value.SequenceNumber ascending
                        select pair;

            variables._cvariables = items.ToDictionary(r => r.Key, r => r.Value);
             
                        
        
        }


Es gibt die public Methode Sort die die private Methode sort2 aufruft, welche sich wiederum recursiv aufruft. Diese brauche ich nur dafür. Da sort2 nicht schön ist, versuche ich sie nun mit Lambda zu elemenieren:

Code:
private delegate void del(cVariables variables);
        public void Sort()
        {
            
            
            del recursiveRun = (cVariables v) =>
            {
                foreach (cVariable variable in v)
                    recursiveRun(variable.Variables); //Verwendung der nicht zugewiesenen lokalen Variablen "recursiveRun"

                dosort(v);
            };

            recursiveRun(this); 
        }

Das funktioniert so noch nicht, weil ich recursiveRun nicht innerhalb der Methode ausführen kann. Wie muss das korrekterweise aussehen?

Btw. ist ein delegate nicht mindestens genau so unschön wie so eine sort2 Methode ?
Ergänzung ()

Ein Hinweis noch: Die Klasse cVariables ist selbst ein Aufzählungsobjekt. Es implementiert IEnumerable, ISerializable. Es ist ein hierarchisches Objekt.
 
Wenn es dir wirklich darum geht, Basics zu verstehen, würde ich an deiner Stelle auf Rekusion erstmal verzichten. Ansonsten bittet Google da genug Lösungen, z.B: Klick
 
@All: Danke für Eure Antworten.

@tshape: Deine Links helfen mir sehr weiter. Eine anonyme Rekursion sieht wirklich erstmal heftig aus. Ich werd mich da mal durcharbeiten und sehen ob ich die sort2 Methode durch Lambda ersetzt bekomme. Wenn ich das geschafft habe, dann habe ich es glaub ich verstanden. Btw. nutzt Du Lambdas selbst (um beispielsweise Code zu kürzen) oder nur wenn Du unbedingt musst ?

Gruß Magic
 

Ähnliche Themen

Antworten
8
Aufrufe
20.084
Stefan_Sch
S
Zurück
Oben