Funktionaleprogrammierung Übungsaufgaben
-
qwertzuiopü schrieb:
Wie würde man in Haskell eigentlich einen Bankautomaten richtig funktional programmieren bei dem man speichern will, wieviel Geld er noch enthält? Wenn ich Geld abheben will, müsste ich den Wert ja eigentlich ändern, aber wenn ich es pure functional programmieren will, müsste der Wert ja immutable sein. Wie macht man sowas richtig funktional?
Zum Beispiel so:
withdraw :: Integer -> Account -> Account withdraw n (acc@Account { amt = a }) = acc { amt = a - n }
-
Das müsste ich doch dann so aufrufen, dass wieder ein neuer Account erstellt wird. Oder geht das anders?
let accNew = withdraw 5 accOld
Und ist das nicht die objektorientierte Art zu programmieren, wenn man ein Account Objekt hat und damit was macht? Gibts da keine funktionale Idee?
-
Was für Übungsaufgaben sind gut um Funktionaleprogrammierung zu lernen?
Die mitgelieferten in einem guten Buch.
volkard schrieb:
Alle außer Webserver und Betriebssysteme.
Brian Beckman meinte mal, er habe Scheme in Betriebssytemschedulern eingesetzt. Fuer Webserver und Webshops hat Paul Graham erfolgreich Lisp eingesetzt.
qwertzuiopü schrieb:
Das müsste ich doch dann so aufrufen, dass wieder ein neuer Account erstellt wird. Oder geht das anders?
let accNew = withdraw 5 accOld
Und ist das nicht die objektorientierte Art zu programmieren, wenn man ein Account Objekt hat und damit was macht? Gibts da keine funktionale Idee?
Das ist doch die "reine funktionale Idee", ein neues Account-Objekt zu erstellen. Auch streiten sich die Geister bei der "funktionalen Idee", genauso wie bei objektorientiert.
-
Und wie mache ich dann weiter? Angenommen ich will, dass der Benutzer Geld abheben und einzahlen kann. Da kann ich doch nicht immer ein neues Account-Objekt erstellen. Soll ich eine rekursive Funktion machen, die eine Abfrage hat "einzahlen oder Abheben", dann ein neues Objekt erstellen und die Funktion wieder damit aufrufen? Ist das nicht eine riesen Speicherverschwendung? Wie macht man sowas funktional?
-
qwertzuiopü schrieb:
...Ist das nicht eine riesen Speicherverschwendung? Wie macht man sowas funktional?
kann man nicht tail? rekursive funktionen in iterative umformen? dann wär das kein problem... man muß nur schauen dass das auch passiert... sonst knallts früher oder später
-
tail/? rekursive
-
ein neues Objekt erstellen und die Funktion wieder damit aufrufen? Ist das nicht eine riesen Speicherverschwendung? Wie macht man sowas funktional?
Wieso, das alte Objekt wird durch den garbage collector eingesammelt, bzw. automatisch wiederverwendet. Auch gibt es Sprachen, die direkte Wertzuweisung mittels set! oder aehnlichem zulassen. Ansonsten: Beschaeftige dich einfach mit den Grundlagen. Die Frage "Wie macht man sowas funktional?" laesst vermuten, dass du dich nicht genug damit beschaeftigt hast. Oder aber nur rumtrollst.
-
Ich hab mal ein kleines "Bankautomaten" Programm geschrieben. Was kann man da noch verbessern?
data Account = Account { amt :: Integer } withdraw :: Integer -> Account -> Account withdraw n (acc@Account { amt = a }) = acc { amt = a - n } deposit :: Integer -> Account -> Account deposit n (acc@Account { amt = a }) = acc { amt = a + n } doBanking :: Integer -> Integer -> Account -> Account doBanking nr value acc | nr == 1 = withdraw value acc | nr == 2 = deposit value acc | otherwise = acc doInput :: Account -> IO () doInput acc = do putStr "\n\nCurrent amount: " print (amt acc) putStrLn "\n1 = withdraw\n2 = deposit\n0 = Exit" nr <- readLn if nr > 0 then do putStrLn "Value:" value <- readLn let newAcc = doBanking nr value acc doInput newAcc else do putStrLn "End" main = do let acc = Account 9 doInput acc
-
qwertzuiopü schrieb:
Wie macht man sowas funktional?
Eigentlich haben wir dort zwei Probleme:
Das erste ist, dass man das Objekt natürlich ändern muss. Das zweite ist die Reihenfolge. Echt funktionale Sprachen arbeiten nach dem theoretischen Prinzip der Ausdrucksreduktion, da gibt es keine Abarbeitungsreihenfolge wie in imperativen Sprachen mehr. Jeder Ausdruck kann zu einem beliebigen Zeitpunkt in beliebiger Reihenfolge ausgewertet werden. Was für normale Berechnungen recht cool ist, hat natürlich bei Datenveränderungen schlimme Auswirkungen
Um beides zu lösen benutzt Haskell Monaden. Das Prinzip ist eigentlich, wie hier bereits vorgestellt: Monadenobjekte werden durch jede Operation nur kopiert und verändert, nicht direkt modifiziert. Der Trick ist nun, dass der Compiler weiß: "das ist eine Monade, also kann ich alle Operationen direkt sinnvoll auf das ursprüngliche Objekt umbiegen". Der Reihenfolgekonstraint wird so gelöst, dass der Ausdruck "erst A dann B dann C" so umgeformt wird, dass er die Form A(B(C(...))) hat und dadurch jeder Ausdruck über das Monadenobjekt direkt auf das Ergebnis der Vorgänger angewiesen ist. Natürlich mit einer Menge Syntaxzucker. Vergleiche dazu den BMI-Rechner auf der letzten Seite. IO-Operationen werden in Haskell durch Monaden erledigt.
-
Was kann man da noch verbessern?
Wenn man das lernen will, sollte man objektorientiert erstmal links liegen lassen, also auch die Aufgabenstellungen. Habe ich eine Liste von Accounts und aendere ein Objekt mittels withdraw, dann ist in dieser Liste immer noch das alte Account-Objekt. Ich muesste das alte entfernen und das neue hinzufuegen. Dann habe ich aber auch eine neue Liste. Hat jetzt irgendwas auf diese Liste verwiesen, so verweisst der Link dann immer noch auf die alte Liste. Irgendwie hat man dadurch nichts gewonnen. Deswegen wuerde ich auch IORefs benutzten oder aber STM. Leider ist man dann durch IO wieder in der imperativen Welt. Nix funktional. Bankaccounts sind ein schlechtes Beispiel, um funktionale Programmierung zu ueben.
Ansonsten: Explizite Rekursion vermeiden,
forever
im Mainloop benutzen und mittels Exception oder Continuation ausbrechen. Account-Interface anbieten, falls mal unterschiedliche Accounts behandelt werden sollen. Insgesamt: Haskell Study Plan