Hugs (Haskell) < Haskell < Programmiersprachen < Praxis < Informatik < Vorhilfe
|
Hi!
Ich habe ein paar Fragen zu dem Programm Hugs von Haskell. Ich nehm das grad im Moment in der Schule durch. Hab damit aber große schwierigkeiten. Da soll es beispielsweise pattern matching geben. Ich hab mir dazu einige webseiten rausgesucht aber erklären konnten sie mir das Problem nicht. Desweiteren hab ich gelernt, dass man beispielsweise so etwas hier schreiben kann " fak (x,y)" wo ist bei dem der unterscheid zu dem : " fak x y" dann gibt es ja auch noch "fak (x + 1)" Ich versteh das einfach nicht :( Ebensowenig wie ich eine Funktion "bauen" kann, die ihre endergebnis einer anderen gibt. Ich hab hier noch ein Beispiel, dass ich gefunden habe, aber trotz der erläuterung nicht versteh.
fak 1 = 1
fak ( x + 1) = (x + 1) fak x
Wieso zwei mal x + 1?
Desweiten habe ich da noch eine Aufgabe. Man soll eine Funk. schreiben, welche nach der eingegebenen int-zahl x-viele * abbildet. Ich dachte mit einer rekursion aber irgendwie sagt er da immer die typen passen nicht :S
Es wäre sehr lieb, wenn einer von euch mir diese ganzen Fragen beantworten könnte! Vielen Dank schon mal im Voraus!!!
Gruß WaterofLife
PS:Ich habe diese Frage in keinem Forum auf anderen Internetseiten gestellt.
|
|
|
|
Status: |
(Antwort) fertig | Datum: | 13:59 Sa 19.11.2005 | Autor: | Frank05 |
> Ich habe ein paar Fragen zu dem Programm Hugs von Haskell.
Haskell ist eine Programmiersprache, keine Firma.
> Ich nehm das grad im Moment in der Schule durch. Hab damit
> aber große schwierigkeiten.
Das ist auch so gedacht
Haskell macht am Anfang eigentlich so ziemlich jedem Schwierigkeiten, aber das wird schon mit der Zeit.
> Da soll es beispielsweise
> pattern matching geben. Ich hab mir dazu einige webseiten
> rausgesucht aber erklären konnten sie mir das Problem
> nicht.
Erklärung siehe bei deinem Beispiel weiter unten.
> Desweiteren hab ich gelernt, dass man beispielsweise
> so etwas hier schreiben kann " fak (x,y)" wo ist bei dem
> der unterscheid zu dem : " fak x y" dann gibt es ja auch
> noch "fak (x + 1)" Ich versteh das einfach nicht :(
Das hat mit Pattern Matching nichts zu tun, sondern mit der Typisierung:
fak (x, y) -- Hier wird eine Funktion fak aufgerufen und sie bekommt als Eingabe genau einen Parameter in Form des Tupels (x, y)
fak x y -- Hier wird eine Funktion fak aufgerufen und bekommt zwei Parameter als Eingabe
fak (x+1) -- Hier wird eine Funktion fak mit einem Parameter aufgerufen. Dieser Parameter wird bei Bedarf berechnet als x+1
fak x + 1 -- Hier würde fak mit dem Parameter x aufgerufen und zum Ergebnis 1 addiert.
> Ebensowenig wie ich eine Funktion "bauen" kann, die ihre
> endergebnis einer anderen gibt.
Nun das Weitergeben eines Ergebnisses ist ganz einfach:
f1 (f2 x y) z -- damit rufst du f2 mit x und y auf und f1 mit dem Ergebnis und z
Interessanter findest du vielleicht dann Funktionen höherer Ordnung. Daran muss man sich erst noch etwas gewöhnen, aber wenn die anfängliche Unsicherheit mal überwunden ist sind solche Funktionen unwahrscheinlich gut. Aber das werdet ihr sicherlich noch demnächst genauer behandeln, was es damit auf sich hat.
> Ich hab hier noch ein
> Beispiel, dass ich gefunden habe, aber trotz der
> erläuterung nicht versteh.
>
> fak 1 = 1
> fak ( x + 1) = (x + 1) fak x
>
> Wieso zwei mal x + 1?
Das ist ein etwas konstruiertes Beispiel, dass du da aufgetrieben hast. Lass mich deshalb mal die gängigere Version hier behandeln:
fak :: Int -> Int
fak 1 = 1
fak x = x * fak (x-1)
Hier kommt jetzt dein besagtes Pattern Matching ins Spiel. Wenn du nun die Funktion aufrufst gibt es ja zwei Möglichkeiten, was passieren kann. Und diese müssen irgendwie unterschieden werden. Das passiert in Haskell, indem du den Eingabeparameter versuchst auf das Pattern (den Teil links vom = ) zu matchen. Wenn du also "fak 1" aufrufst wird geprüft ob die Eingabe-1 das gleiche ist wie die obige 1 des fak Parameters. Da das der Fall ist wird als Ergebnis auch 1 geliefert (und der zweite Fall nie angerührt). Rufst du "fak 2" auf, dann passt die erste Zeile eben nicht mehr. Dann wird mit der zweiten verglichen. Dort gibt es eine Variable x vom Typ Int und die kann nun auf 2 gesetzt werden, dann passt wieder alles zusammen. Also wird die rechte Seite ausgewertet als "2 * fak (2-1)". Wie du hier schon siehst wird das Pattern Matching bei jedem - auch rekursivem - Aufruf der Funktion angewendet.
Bei deinem Beispiel würde für "fak 2" das x eben nur auf den Wert 1 gesetzt, damit das Pattern wieder passt.
> Desweiten habe ich da noch eine Aufgabe. Man soll eine
> Funk. schreiben, welche nach der eingegebenen int-zahl
> x-viele * abbildet. Ich dachte mit einer rekursion aber
> irgendwie sagt er da immer die typen passen nicht :S
Auch daran muss man sich gewöhnen. Typen sind das A und O in Haskell. Bevor du über die Funktion, die du schreiben willst, nachdenkst _musst_ du dir erst Gedanken um ihren Typ machen. In diesem Fall soll es eine Funktion werden, die eine Int Zahl bekommt und dann einen String zurückliefert:
f :: Int -> String
Und mit Rekursion liegst du schon richtig. Nun kommst du an den Punkt, wo das Patternmatching wieder von Interesse ist (auf diese Art wird z.B. sehr gern die Abbruchbedingung der Rekursion realisiert). Wann bricht die Rekursion denn ab? Genau, wenn diese Int Zahl 0 ist brauch ich keine Sterne mehr ausgeben:
f 0 = ""
Die allgemeine Rekursion gilt somit für alle anderen Werte, die die Eingabezahl annehmen kann und gibt einen zusätzlichen Stern aus:
f x = '*' : f (x-1)
oder falls ihr den : Operator noch nicht hattet:
f x = "*" ++ f (x-1)
|
|
|
|
|
Danke für deine schnelle Antwort!
Ich denke mir sind jetzt ein paar Dinge schon etwas klarer geworden :).
Aber ich hab noch ein paar Fragen, worauf ich keine Antwort weiß. Ich werd sie mal nummierieren der Übersicht halber.
1. Bei der Variablendeklaration z.B. von zahl x y und zahl (x,y). Wo liegt da der Unterschied, außer, dass das Anwender bei dem Tupel es anders eingeben muss?
2. Könnt ihr mir vielleicht noch ein mal ausfürhlich den Unterschied zwischen eines Mustervergleiches und einer Bedingung erkären?
3. Bei einer rekursion, muss die Rekursionsprozedur ja wissen, was sie genau "wiederholen" soll. beispielswiese bei summevon7bis x = x + summevon7bis (x - 1)
Heißt es dann sie wiederholt immer nur das, was in der klammer nach der rek. kommt? Weil sie rechnet ja nicht bei jedem (x - 1) das x hinzu. Denk ich zumindest. Ich hab da irgendwie schwierigkeiten bei dem schreiben einer Rekursion. Gibt es da irgendwie ein Prinzip, dem man folgen kann?
4. Ich habe hier eine Übungsaufgabe aus einem Schulbuch. Da soll man eine Zeichenkette auf den Buchstaben a prüfen, natülich rekursiv.
Hier mein Lösungsansatz:
a_test 'a' = True
a_test "" = False
a_test x = a_test (head (tail x)
rein von der theori müsste es ja klappen er nimmt jedes mal ein Buchstaben prüft dann die zwei abbruchbedingungen ab und nimmt den nächsten. Nur er zeigt immer bei mir diese fehlermeldung an:
*** Expression : a_test tail x
*** Term : a_test
*** Type : Char -> Char
*** Does not match : a -> b -> c
Irgendetwas mach ich immer falsch nur ich weiß nciht was. Mein Lehrer meinte ein mal ich bin noch zu sehr in der imperativen programmiersprache, vielleicht stimmt das aber helfen tut es mir nicht...
5. Wie geht man eigentlich allgemein solche Aufgaben an?
Bei mir scheitert es ja nciht an dem logischen verstand, sondern ich weiß einfach nciht, wie ich es mit dieser neuartigen sprache schreiben soll...
7. eine letzte frage hab ich noch beziehend der musteranpassung. ich hab hier zwei lösungen einer aufgabe von meinem lehrer. Wie nennt man da denn welche weil es sind ja anscheinend vberscheidene schreibweisen und woran kann ich das erkennen?
1) istgleichdrei' :: Int -> Bool
istgleichdrei' zahl = (zahl == 3)
2) istgleichdrei'' 3 = True
istgleichdrei'' sonstetwas = False
Wäre sehr dankbar, wenn ihr meine Fragen beantworten könntet!
Gruß WaterofLife
|
|
|
|
|
Status: |
(Antwort) fertig | Datum: | 21:03 Sa 19.11.2005 | Autor: | Frank05 |
> Aber ich hab noch ein paar Fragen, worauf ich keine Antwort
> weiß. Ich werd sie mal nummierieren der Übersicht halber.
>
> 1. Bei der Variablendeklaration z.B. von zahl x y und zahl
> (x,y). Wo liegt da der Unterschied, außer, dass das
> Anwender bei dem Tupel es anders eingeben muss?
Typen. Gaaaanz wichtige Sache
Im Fall "zahl x y" hast du zwei verschiedene Argumente, die an die Funktion "zahl" gegeben werden. Im Fall "zahl (x, y)" nur ein Argument. Das heißt im ersten Fall hat Zahl den Typ "zahl :: a -> b -> c" und im zweiten Fall "zahl :: a -> b".
> 2. Könnt ihr mir vielleicht noch ein mal ausfürhlich den
> Unterschied zwischen eines Mustervergleiches und einer
> Bedingung erkären?
Eine Bedingung ist das gleiche wie in imperativen Sprachen. Irgendeine Überprüfung und dann kommt ein boolescher Wert dabei raus. Der Mustervergleich stellt gleichzeitig aber noch eine Variablenbindung her.
foo 3 = True
Ist nicht viel anders als eine Bedingung: Prüfe ob Eingabevariable == 3, dann gib True zurück.
foo x = ...
Das hingegen bindet die Eingabe an die Variable x (die dann im rechten Teil weiter verwendet werden kann).
Später kannst du das auch für komplexere Sachen verwenden:
foo (x:xs) = ..
Das erwartet dann eine Liste als Eingabe und bindet das erste Element in dieser Liste an die Variable x und die verbleibende Restliste an xs.
Im Prinzip sind diese Mustervergleiche eine einfache Möglichkeit Funktionen fall-basiert zu definieren (spart also das aus imperativen Programmen bekannte if-then-else if...else if...) und erhöht somit deutlich die Lesbarkeit der Funktionen. Vorerst würde ich dir empfehlen erstmal so gut es geht ohne explizite Angabe von Bedingungen auszukommen (wie dein Lehrer wohl schon sagte ist das eher imperativer Stil). An manchen Stellen geht das vielleicht nicht, aber sehr häufig kann man schon alles was man unterscheiden will über eine musterbasierte Definition der Funktion erreichen.
> 3. Bei einer rekursion, muss die Rekursionsprozedur ja
> wissen, was sie genau "wiederholen" soll. beispielswiese
> bei summevon7bis x = x
> + summevon7bis (x - 1)
> Heißt es dann sie wiederholt immer nur das, was in der
> klammer nach der rek. kommt? Weil sie rechnet ja nicht bei
> jedem (x - 1) das x hinzu. Denk ich zumindest. Ich hab da
> irgendwie schwierigkeiten bei dem schreiben einer
> Rekursion. Gibt es da irgendwie ein Prinzip, dem man folgen
> kann?
Nennen wir die Funktion der Einfachheit halber mal f:
f :: Int -> Int
f x = x + f (x-1)
Dass hinter der Funktion hier was in Klammern steht ist vollkommen egal. Entscheidend ist wieder der Typ der Funktion. f ist so definiert, dass sie einen Parameter vom Typ Int erwartet und dann ein Ergebnis vom Typ Int liefert. Was also hinter f stehen muss ist irgendein Parameter vom Typ Int. Ob das in Klammern dasteht oder sonstwie ist erstmal nicht wichtig. Hier nochmal etwas anders geschrieben, damit du den Unterschied siehst:
f x = x + f y
where y = x-1
Hier geht es ja nur darum erst vom aktuellen Wert 1 abzuziehen, bevor man mit diesem kleineren Wert dann die Rekursion startet.
Was dann dieser Aufruf von f mit dem kleineren Wert tatsächlich macht hängt wieder von der Definition dieser Funktion ab. Wenn da nur diese eine Zeile stehen würde, dann hört die Rekursion eigentlich nie auf. Daher gibt es diese Muster, über die man die Rekursion beenden kann, indem man auf der rechten Seite eben etwas schreibt, in dem f selbst nicht mehr auftritt:
f 0 = 0
Was das Prinzip der Rekursion anbelangt ändert sich nicht viel: Du überlegst zuerst die allgemeine Variante, dann wann die Rekursion abbricht. Musst nur darauf achten, dass der Abbruchsfall bei den musterbasierten Version auch zuerst kommt:
f1 0 = 0
f1 x = x + f1 (x-1)
f2 x = x + f2 (x-1)
f2 0 = 0
f1 terminiert, aber f2 eben nicht.
> 4. Ich habe hier eine Übungsaufgabe aus einem Schulbuch. Da
> soll man eine Zeichenkette auf den Buchstaben a prüfen,
> natülich rekursiv.
> Hier mein Lösungsansatz:
>
> a_test 'a' = True
> a_test "" = False
Err.. Typfehler
'a' ist ein Char, während "" ein String (also [Char]) ist.
stattdessen ['a'] oder ""
> a_test x = a_test (head (tail x)
Hier fehlt mal mindestens eine schließende Klammer. Außerdem bringt es wenig NUR das 2te zeichen zu betrachten. Hier bietet sich die musterbasierte Implementierung für Listen an:
a_test ('a':xs) = True
a_test (x:xs) = a_test xs
a_test _ = False
> rein von der theori müsste es ja klappen er nimmt jedes mal
> ein Buchstaben prüft dann die zwei abbruchbedingungen ab
> und nimmt den nächsten.
Nein. Es wird nicht jedesmal der nächste genommen. Du nimmst nur einmal den zweiten Buchstaben mit head (tail x) !
> Nur er zeigt immer bei mir diese
> fehlermeldung an:
>
> *** Expression : a_test tail x
> *** Term : a_test
> *** Type : Char -> Char
> *** Does not match : a -> b -> c
Richtig. Deine Typen passen hier hinten und vorne nicht. Deshalb vorher die Typisierung der Funktion angeben! Und dann kannst du auch selber nochmal nachsehen, wo es passt und wo nicht.
a_test :: String -> Boolean
> Irgendetwas mach ich immer falsch nur ich weiß nciht was.
> Mein Lehrer meinte ein mal ich bin noch zu sehr in der
> imperativen programmiersprache, vielleicht stimmt das aber
> helfen tut es mir nicht...
Das mag sein.. hat aber in dem konkreten Fall gar nix zu suchen. Du hast schlicht und einfach keine Sorgfalt bei den Typen walten lassen.
> 5. Wie geht man eigentlich allgemein solche Aufgaben an?
> Bei mir scheitert es ja nciht an dem logischen verstand,
> sondern ich weiß einfach nciht, wie ich es mit dieser
> neuartigen sprache schreiben soll...
Typisierung überlegen
Dann die nötigen Sonderfälle musterbasiert abhandeln.
Danach noch was im allgemeinen Fall gemacht werden soll.
6. Fehlt ?
> 7. eine letzte frage hab ich noch beziehend der
> musteranpassung. ich hab hier zwei lösungen einer aufgabe
> von meinem lehrer. Wie nennt man da denn welche weil es
> sind ja anscheinend vberscheidene schreibweisen und woran
> kann ich das erkennen?
>
> 1) istgleichdrei' :: Int -> Bool
> istgleichdrei' zahl = (zahl == 3)
>
> 2) istgleichdrei'' 3 = True
> istgleichdrei'' sonstetwas = False
Ich weiß nicht, was du meinst mit 'wie nennt man die Lösungen'.. das erste ist eben eine klassische (imperativ begründete wenn du so willst) Lösung mittels einer Bedingung. Das zweite verwendet die musterbasierte Definition und entspricht somit eher dem, was man in Haskell gerne sehen würde. Aber an sich sind beide Lösungen in Ordnung. In dem Fall wäre es mit expliziter Bedingung kürzer geschrieben, aber spätestens wenn du eine Funktion istgleichdreisiebenoderachtzehn definierst wirst du den deutlichen Unterschied in der Lesbarkeit erkennen können.
|
|
|
|