Disclaimer: Dieser Thread wurde aus dem alten Forum importiert. Daher werden eventuell nicht alle Formatierungen richtig angezeigt. Der ursprüngliche Thread beginnt im zweiten Post dieses Threads.
Aufgabe 12.3
Wegen baldigen Klausuren hab ich mich jetzt schonmal ans letzte Übungsblatt gewagt und hätte eine Frage zur 12.3:
Ich habe verstanden, was uns in der Vorlesung zu foldRight erklärt wurde und habe auch ein Beispiel geschrieben, wie ich die foldLeft Funktion bei Maps verwende. Aber ich sehe noch nicht, wie mir die Funktion helfen kann. Meineswissens kann ich damit Einträge zusammenfügen (mit + oder *) wie es in der Vorlesung war, oder bestimmt auch Einträge konkattenieren, aber was hilft mir das dann? Die Zahlen aus dem Beispiel summieren wäre ja quatsch Für die Keys habe ich auch eine Funktion, mit der ich die bekommen. Die Values kann man mit foldLeft bestimmt als Liste rausholen, aber des geht anders ja auch. Kann mir da jemand einen Tipp geben, oder muss ich die Faltungsfunktionen noch ganz anders betrachten. Vllt ist meine Sicht durch die Vorlesung zu eingeschränkt. Ich würde es ja auch anders probieren, aber anscheinend MUSS ich ja foldLeft verwenden…
Und würde es mit foldRight nicht ganz genauso gehen oder wieso muss es unbedingt foldLeft sein?
foldRight und foldLeft sind gleichwertig und unterscheiden sich lediglich in der Abarbeitungsreihenfolge (links->rechts vs. rechts->links) und in der Signatur (Reihenfolge der Parameter der inneren Funktion sind vertauscht).
(Anmerkung am Rande: Es gibt noch eine dritte Funktion, die sich nur fold nennt. Bei dieser bestimmt die Implementierung die Abarbeitungsreihenfolge, so dass die übergebene Operation assoziativ, d.h. in beliebiger Reihenfolge anwendbar sein muss. Dadurch kann die Faltung dann auch parallelisiert ausgeführt werden => informell: quasi Rekursion in parallel.)
Die Aufgabe soll explizit mittels foldLeft gelöst werden, auch wenn es auch mit foldRight oder irgendeiner anderen Möglichkeit machbar wäre. Wenn man auf einen Alternativweg ausweicht, lernt man das vermiedene Konzept nicht.
Mit Hilfe einer Faltungsoperation lässt sich Rekursion verkürzt schreiben. Das sollte mit der Übungsaufgabe 12.1 deutlich werden. Wer die Faltung verstanden hat, spart sich Rekursion. Die Faltung ist dabei derart mächtig, dass man alle anderen, durch Rekursion ausdrückbaren Funktionen, wie z.B. map, flatMap, filter, etc. auf eine Faltung zurückführen kann. Deshalb verlangen wir bei einigen Aufgaben oft ausdrücklich Rekursion, da man sonst mit einem Einzeiler wegkäme - dann aber wieder ohne vielleicht Rekursion verstanden zu haben.
Auch Google kann beim Problem Faltung (fold in der Informatik, nicht zu Verwechseln mit der Faltung in der Mathematik) helfen: http://en.wikipedia.org/wiki/Fold_(higher-order_function)
Danke, dann hab ich den Sinn schonmal verstanden. Nur sehe ich immer noch nicht, was ich “falten” soll.
Wäre es dann ein logischer Weg, zuerst zu versuchen, die Aufgabe mittels Rekursion zu programmieren, damit einem klar wird, wie man das Problem berechnet, und dann erst die Faltung einbaut? Oder soll man sich zunächst an der Aufgabe 12.1 probieren? Ich sehe einfach noch nicht, was ich hier “falten” soll…
Die Aufgabe 12.1 soll dabei helfen das Prinzip der Faltung zu verstehen.
Es wäre natürlich immer von Vorteil alle Übungsaufgaben zu machen. Oft bereiten die Nicht-Bonusaufgaben den Stoff der Bonusaufgaben vor. Z.B. behandelt 12.2 die Streams, welche man in der zweiten Bonusaufgabe von Blatt 12 benötigt.
Also kaputt ist das ganze schon oder?
val target = Map[String, Set[Int]]()
bei target einen Eintrag einfügen
val targetOne = target + ("PFP" -> Set(1))
Mit der selben Syntax in das Set ein Element anfügen
val targetOneAmend = targetOne + ("PFP" -> res1("PFP") + 2)
funktioniert natürlich nicht!
Stattdesen:
val targetOneAmend = targetOne + (("PFP", res1("PFP") + 2))
Scala ist schon sehr merkwürdig
Das hat mit der Operatorenreihenfolge zu tun: “->” wird zuerst angewendet und wandelt die beiden Operanden in ein Tupel. a → b ist gleichwertig mit (a, b). Auf dieses Tupel möchtest du dann 2 draufzählen was natürlicht nicht geht. Man muss den zweiten Operanden deshalb klammern:
val targetOneAmend = targetOne + ("PFP" -> (res1("PFP") + 2))
In deinem zweiten Beispiel brauchst du zwei äußere Klammern aufgrund der Signatur von +: + erwartet eine Sequenz von Key-Value-Paaren. Gibt man nur ein Tupel, also targetOne + (a, b) an, dann sieht Scala hier eine Sequenz von zwei Tupeln a und b. a und b sind in deinem Fall aber keine Tupel => nochmal ein paar Klammern drumherum.
So wie ich dich verstehe sollte es so:
val targetOneAmend = targetOne + (("PFP", res1("PFP") + 2))
nicht funktionieren. Damit löse ich aber die Aufgabe …
Das verwirrt mich jetzt sehr
Dann habe ich mich falsch ausgedrückt.
Doch, so sollte es gehen: die erste Klammer sagt der ±Operation, dass eine Sequenz von Key/Value-Paaren beginnt, die hinzugefügt werden sollen. Die zweite Klammer umfasst das erste Key/Value-Paar, und ist also ein Tupel. Der 2. Operand in diesem Tupel kann gerne ein komplexer Ausdruck sein, da durch das einleitende Komma und die abschließende Klammer der Umfang dieser komplexen Operation klar abgegrenzt ist. Was aber nicht geht ist eben ein komplexer Ausdruck zusammen mit dem “->”-Operator.
Nochmal zum direkten Vergleich, folgende 3 Varianten sind gleichwertig
val targetOneAmend = targetOne + (("PFP", res1("PFP") + 2))
val targetOneAmend = targetOne + (("PFP", (res1("PFP") + 2)))
val targetOneAmend = targetOne + ("PFP" -> (res1("PFP") + 2))