Autor |
Beitrag |
AndrThadk
Hält's aus hier
Beiträge: 3
|
Verfasst: Mi 06.02.13 21:31
Hallo,
ich hoffe ich bin hier in der Ecke richtig. Ich hab ein Problem bei der Handhabung von Arrays.
Und zwar habe ich heute eine Informatik Klausur geschrieben, die an sich ziemlich einfach war,
nur eine bestimmte Aufgabe hat mir Kopfzerbrechen bereitet und tut es noch immer.
Und zwar sollten wir zwei Arrays erstellen,
- 1 x String Array gefüttert mit 4 Werten (in meinem Beispiel unten hab ich der Einfachkeit halber a,b,c,d genommen)
- 1 x Double Array, ebenfalls mit 4 Werten (ebenfalls Beispielwerte unten).
Anschließend sollten die Werte aus den beiden Arrays in folgender Form in der Konsole ausgegeben werden:
Zitat: |
Überschrift:
[Wert aus Array1]: [Wert aus Array2], also:
a: 1,1
b: 2,2
c: 3,3
d: 4,4
|
Ich hab die Aufgabenstellung dann wie folgt gelöst:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| String[] ListeLetters = new String[] { "a", "b", "c", "d" }; Double[] ListeNumbers = new Double[] { 1.1, 2.2, 3.3, 4.4, };
int x = 0; for (x = 0; x < ListeLetters.Length; x++) { Console.WriteLine(ListeLetters[x] + ": " + ListeNumbers[x]); } |
So, das funktioniert wunderbar und liefert mir in der Konsole das in der Aufgabenstellung geforderte Bild.
Ob das jetzt in der Klausur volle Punktzahl gibt oder Abzüge, weil ich nachfolgendes nicht beachtet habe, lass ich mal offen...
Mein Problem dabei ist nur, dass es problematisch wird, wenn die Arrays nicht "zufällig" die gleiche Länge haben.
Wenn man in meinem Beispiel die Länge der Liste ListeLetters, die ich mit meiner Zählvariable "x" vergleiche, größer macht als die Länge der anderen Liste, erhalte ich einen Fehler. Wenn man wiederum der ListeNumbers in meinem Beispiel mehr Werte zuordnet als der ListeLetters, so funktioniert es weiterhin einwandfrei.
Mein Gedanke nun, ich programmier das so, dass es auch bei unterschiedlicher Array Länge funktioniert, unabhängig davon welches Array größer oder kleiner ist. Gedacht hab ich mir das so, nämlich das zB in folgendem Fall:
C#-Quelltext 1: 2:
| String[] ListeLetters2 = new String[] { "a", "b", "c", "d", "e", "f", "g" }; Double[] ListeNumbers2 = new Double[] { 1.1, 2.2, 3.3, 4.4, }; |
So eine Ausgabe entsteht:
Zitat: |
Überschrift:
a: 1,1
b: 2,2
c: 3,3
d: 4,4
e: no data
f: no data
g: no data
|
Oder andersrum. Buchstabe unbekannt, aber Zahl vorhanden.
Mein Problem nur, ich zerbrech mir da schon lange den Kopf, mein Klausurskript, alle Übungsaufgaben, Google und die msdn Dokumentation haben mir nicht weitergeholfen.
Ich wäre froh, wenn einer von euch mich erleuchten könnte, wie ich das so anstellen kann, wie ich mir das vorstelle.
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mi 06.02.13 22:39
Hallo und  !
Eine recht sperrige Lösung wäre wohl das hier:
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:
| int whichIsSmaller; if (ListeLetters.Length == ListeNumbers.Length) whichIsSmaller = 0; else if (ListeLetters.Length < ListeNumbers.Length) whichIsSmaller = -1; else whichIsSmaller = 1; var minLength = Math.Min(ListeLetters.Length, ListeNumbers.Length); var maxLength = Math.Max(ListeLetters.Length, ListeNumbers.Length);
for(int i = 0; i < minLength; i++) Console.WriteLine(ListeLetters[i] + ": " + ListeNumbers[i]);
for(int i = minLength; i < maxLength; i++) if (whichIsSmaller == -1) Console.WriteLine("n/a" + ": " + ListeNumbers[i]); else Console.WriteLine(ListeLetters[i] +": " + "n/a"); Console.ReadLine(); |
Also erst gucken, welches Array das Kürzere ist. Dann erstmal die Daten ausgeben, bei denen es in beiden Arrays was gibt. Und dann den Rest von dem längeren Array. Aber wie gesagt: Eher sperrig.
Kürzer geht es z.B. mit Lambdaausdrücken:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| Func<int, string> GetLetterOrNA = idx => { return idx < ListeLetters.Length ? ListeLetters[idx] : "n/a"; }; Func<int, string> GetNumberOrNA = idx => { return idx < ListeNumbers.Length ? ListeNumbers[idx].ToString() : "n/a"; }; var maxLength = Math.Max(ListeLetters.Length, ListeNumbers.Length); for(int i = 0; i < maxLength; i++) Console.WriteLine(GetLetterOrNA(i) + ": " + GetNumberOrNA(i));
Console.ReadLine(); |
Die Lambdaausdrücke wirst Du wahrscheinlich noch nicht kennen. Du kannst Dir das als Kurzschreibweise für Funktionen denken. Der erste ( GetLetterOrNA) ist so definiert, dass er einen Integer ( idx) als Parameter bekommt und dann entweder das entsprechende Element des Arrays ListeLetters als String zurück gibt oder den String "n/a". Dasselbe gibt's dann noch für die andere Liste. Am Ende muss man dann nur noch mit einer Schleife bis zur maximalen Länge laufen.
Viele Grüße,
Christian
P.S.: Bei der Entwickler-Ecke bist Du auf jeden Fall in der richtigen Ecke 
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mi 06.02.13 22:51
Hallo AndrThadk, ebenfalls
die einfachste Lösung wäre m.E. in der Schleife jeweils den Index mit der Arraylänge zu vergleichen:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| int maxLength = Math.Max(ListeLetters.Length, ListeNumbers.Length);
for (int i = 0; i<maxLength; i++) { if (i < ListeLetters.Length) Console.Write(ListeLetters[i]); else Console.Write("no data");
Console.Write(": ");
Console.WriteLine(); } |
Ist vllt. nicht ganz so performant wie die Lösung von Christian, aber dafür etwas verständlicher.
Statt des if-Ausdrucks kann man für noch kürzeren Code den : ?-Operator benutzen:
C#-Quelltext 1:
| Console.Write(i < ListeLetters.Length? ListeLetters[i] : "no data"); |
P.S: Ich sehe gerade, daß dies der Lambda-Ausdruck-Lösung sehr nahe kommt (aber ich denke, daß für einen Anfänger Lambda-Ausdrücke als schwierig(er) zu verstehen angesehen werden).
|
|
AndrThadk 
Hält's aus hier
Beiträge: 3
|
Verfasst: Do 07.02.13 00:05
Hey Leute, danke schonmal für die Antworten und die Grüße, hatte heute gar nicht mehr mit einer Antwort gerechnet.
Ist schon spät, werde mir das also morgen mal anschauen und dann meine Erfahrung dazu posten. Aber ein stimmt schonmal, den Lambda Ausdruck kenne ich noch nicht. Wird mir aber möglicherweise noch begegnen, denn das was ich bisher in diesem Semester an Informatik hatte, waren Grundlagen - nächstes Semester soll's an's Eingemachte gehen.
|
|
Palladin007
      
Beiträge: 1282
Erhaltene Danke: 182
Windows 11 x64 Pro
C# (Visual Studio Preview)
|
Verfasst: Do 07.02.13 03:06
Die Beiden haben ja eigentlich schon alles gesagt und mir fällt auch keine dritte Lösung mehr ein, wobei ich nie im Leben das so gelöst hätte, wie Christian.
Ich bevorzuge da eher möglichst kurze Schreibweise, deshalb mag ich die Lambda-Ausdrücke so gerne ^^
Weil jetzt aber schon eigentlich die Frage beantwortet wurde, erkläre ich einfach nur kurz ?:-Operator und grob die Lambda-Ausdrücke.
Der ?:-Operator lässt sich eigentlich ganz einfach mit folgendem Code vergleichen:
C#-Quelltext 1: 2:
| if(BEDINGUNG) return WERT1; else return WERT2; |
Denn nichts anderes macht, dieser Operator. Er wertet einen boolischen Wert aus und gibt einen von zwei Werten zurück, die Syntax sieht dabei so aus:
C#-Quelltext 1:
| BEDINGUNG ? WERT1 : WERT2 |
Also vor dem Fragezeichen steht der boolische Wert, zwischen Fragezeichen und Doppelpunkt das Ergebnis, das zurück gegen wird, wenn die Bedingung true ergibt und hinter dem Doppelpunkt das Ergebnis, wenn sie false ist.
Und das war es eigentlich auch schon. Ich benutze das eigentlich meistens zwischen zwei Strings. In einem Brief kann man das z.B. ganz gut verwenden, wenn mit einem boolischen Wert gespeichert wird, ob der Empfänger männlich oder weiblich ist. Das würde dann so aussehen:
C#-Quelltext 1:
| Console.WriteLine("Sehr geehrte" + (IsFeminin ? "" : "r") + " " + (IsFeminin ? "Frau" : "Herr") + " Sowieso,"); |
Das erspart dir langes Hantieren mit if.
Bei den Lambda-Ausdrücken ist das nicht ganz so leicht.
Lambda-Ausdrücke können zur Erstellung von Delegaten verwendet werden. Delegaten sind wieder ein Thema für sich, deshalb schneide ich das nur kurz an: Delegaten sind sozusagen der Typ, mit dem eine Methode in einer Variable gespeichert werden kann. Dieser Typ wird wie jeder andere Typ auch verwendet. Die Func-Klasse, die Christian verwendet hat, ist eigentlich keine Klasse, sondern ein Delegat, in seinem Beispiel ist das ein Delegat, der eine Methode erwartet, die einen Wert von int als Parameter annimmt und einen string-Wert als Ergebnis zurück gibt. In einer so deklarierten Variable kann man eine Methode auf folgende einfache Weise übergeben:
C#-Quelltext 1: 2: 3: 4: 5: 6:
| public static string DiesIstEineMethode(int par) { return par.ToString(); } Func<int, string> meth = DiesIstEineMethode; Console.WriteLine(meth.Invoke(1); |
Das geht aber auch kürze, mit den Lambda-Ausdrücken. Die verfolgen eine ganz einfache Syntax mit diesem Operator: =>
Vor diesem Operator sind die Parameter einer Methode, nach diesem Operator ist der eigentliche Anweisungsblock.
Den Snippet hätte ich also auch so schreiben können:
C#-Quelltext 1: 2:
| Func<int, string> meth = (int par) => { return par.ToString(); }; Console.WriteLine(meth.Invoke(1); |
Das Ergebnis wäre gleich. In den runden Klammern kann man aber auch mehrere Parameter angeben, durch ein Komma getrennt. In den geschweiften Klammern kann dagegen so viel Code stehen, wie es dir gefällt. Die Frage ist nur, wie sinnvoll viel Code in einem Lambda-Ausdruck ist.
Ich nutze das z.B. ganz gerne, wenn ich Werte in einer Schleife verarbeiten muss und dafür eine Methode sinnvoll ist, ich aber keine erstellen will, weil ich sie sonst nirgendwo mehr brauche. Wenn der Code nicht zu lang ist, schreib ich den in einen Lambda-Ausdruck. Wenn ich z.B. das obige Beispiel bei einem ganzen Array von int-Werten ausführen will:
C#-Quelltext 1: 2: 3:
| int[] arr = new int[] { 1, 2, 3, 4, 5 }; foreach (var item in arr) Console.WriteLine(new Func<int, string>(z => z.ToString()).Invoke(item)); |
Das sieht ziemlich kompliziert aus, daher hier mal auseinander gedröselt:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| int[] arr = new int[] { 1, 2, 3, 4, 5 }; foreach (var item in arr) { Func<int, string> meth = new Func<int, string>((int z) => { return z.ToString(); }); string ergebnis = meth.Invoke(item); Console.WriteLine(ergebnis); } |
Das:
C#-Quelltext
ist also das Gleiche wie das:
C#-Quelltext 1:
| (int z) => { return z.ToString(); } |
Und hier zeigt sich auch der große Vorteil der Lambda-Ausdrücke, denn der Compiler kann hier den richtigen Typ ableiten. Wenn er das nicht kann, muss halt der Typ explizit angegeben werden, aber bei Kurzen Problemen wie z.B. beim Rechnen geht das ganz gut.
Du kannst da auch mehrere Parameter angeben:
C#-Quelltext 1:
| Func<int, int, int, int, int> meth = (a, b, c, d) => a + b + c + d; |
Wenn du keine Parameter hast:
C#-Quelltext 1:
| Func<int> meth = () => 1; |
Du musst das nicht zwingend verstehen oder verwenden, eigentlich ist das nur eine Erleichterung für den Programmierer, um den Code kürzer gestalten zu können. Ich persönlich verwende sie überall, wo es halbwegs sinnvoll ist. Ob das nun ein schlechter Stil ist, oder nicht, soll mir egal sein, es ist mein Stil.
Wenn du es doch verstehen willst und meine kleine Erklärung nicht ausreicht, hab ich hier noch einmal das Kapitel des Buches verlinkt, mit dem ich das gelernt habe.
Wenn du einen Vorsprung vor den Anderen haben willst, kannst du dir ja das ganze Buch anschauen, es liest sich sehr gut und es ist eigentlich alles drin, was du brauchst. Naja, nicht ganz, ein großes Manko ist, dass Windows Forms fehlt, da das für einfachere kleine Anwendungen trotz allem immer noch ganz gut zu gebrauchen ist, aber das gibt es in der 2008-Version zu finden. Es gibt auch bereits eine 2012-Version auf die ich mich auch gleich stürzen werde
PS: Wenn ich Fehler gemacht habe, habt Nachsicht mit mir, es ist spät, da kann sowas schon mal passieren. ^^
Ein kurzer Wink mit dem Zaunpfahl und ich korrigiere den Fehler so bald wie möglich. ^^
|
|
AndrThadk 
Hält's aus hier
Beiträge: 3
|
Verfasst: Fr 08.02.13 20:28
Ok guuut, hab das jetzt soweit mal ausprobiert alles. Vorweg, die Lösung mit den Lambda Ausdrücken, bzw das ganze System dahinter hab ich jetzt auf die Schnelle nicht ausprobiert. Aber es klingt nützlich, also werde ich mich da mal reinlesen.
Zitat: | //Merken, welches der Arrays kleiner ist: -1 = Buchstabenarray, 0 = gleich lang, +1 = Zahlenarray
|
Hier stellt sich mir die Frage, ob das ganze bei dieser Lösung nicht relativ kompliziert werden würde, wenn ich mal mehr als 2 Arrays habe aus denen ich meine Daten ziehe. Dann muss ich ja schon ganz schön viel vergleichen.
Die Lambda Lösung ist da bezüglich der Erweiterungsfähigkeit wahrscheinlich offener, aber damit kann ich, wie gesagt, ja noch nicht umgehen.
Für mich persönlich ist die Lösung von Th69 bisher die für mich am einfachsten nachzuvollziehende und zu erweiternde Lösung, die werd ich mir erstmal merken, bis da andere Techniken beherrsche.
Zu der Verkürzung des if Befehls, frage ich mich dann noch, ob es so eine Möglichkeit auch für if-else if gibt?
C#-Quelltext 1: 2: 3:
| if (Bed) return Wert1; else if (Bed_2) return Wert2; else return Wert3; |
Oder macht das an der Stelle keinen Sinn?
Und gibt es die Kombination if - else if - else?
Also falls das, mache das, falls das andere, mache was anderes, sonst mache was ganz anderes oder so?
Oder würde man da eher zu switch greifen oder sowas?
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Fr 08.02.13 21:21
Hallo,
auch dafür gibt es die Möglichkeit der Verschachtelung beim ? :-Operator:
C#-Quelltext 1:
| return Bed1 ? Wert1 : (Bed2 ? Wert2 : Wert3); |
Und auch dies läßt sich beliebig so fortsetzen (wie bei if else if ...). Die Klammern oben kann man sogar weglassen, dienen aber der besseren Lesbarkeit.
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Fr 08.02.13 21:33
|
|
Palladin007
      
Beiträge: 1282
Erhaltene Danke: 182
Windows 11 x64 Pro
C# (Visual Studio Preview)
|
Verfasst: Sa 09.02.13 05:25
Zitat: | Oder würde man da eher zu switch greifen oder sowas? |
Switch ist ja eigentlich, ich sag mal eine Vereinfachung von langen if-Verschachtelungen, wenn eine Variable mehrere mögliche Zustände haben kann und alle abgearbeitet werden sollen, einschließlich dem, wenn kein bekannter Zustand eintritt.
Wenn eine int-Variable die Zustände -1, 0 und 1 haben kann, dann würde das mit switch so aussehen:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| switch (zahl) { case -1: Console.WriteLine(-1); break; case 0: Console.WriteLine(0); break; case 1: Console.WriteLine(1); break; default: Console.WriteLine(error); break; } |
Das ist gleichbedeutend mit:
C#-Quelltext 1: 2: 3: 4:
| if (zahl == -1) Console.WriteLine(-1); else if (zahl == 0) Console.WriteLine(0); else if (zahl == 1) Console.WriteLine(1); else Console.WriteLine(error); |
Ob das so sinnvoll ist, weiß ich nicht. Ich persönlich nutze switch hauptsächlich, wenn es um Enumerationen geht, oder eine Variable eine größere Menge an immer gleich bleibenden Zuständen haben kann, wie z.B. die Variable, die -1, 0 oder 1 enthalten kann.
|
|
|