www.jammni.de

Logo - Kleiner Drache
Login
Username:

Passwort:

Daten merken
Auto-Login
Registrieren
 
Online
niemand
 
Forumsuche
Suche nach:

Logo - DracheHaskell-Forum

Xan

Gepostet:
21.07.2012 15:12

Kg in Pfund umrechnen mit dem algebraischen Datentyp data  
ich soll kg in Pfund und Pfund in kg umrechnen. 1 Pfund = 0,453 592 370 kg. Ich habe hier eine Funktion geschrieben, die das machen soll. Aber als Fehler wird angezeigt \"umrechne\" multiply defined\"

data Gewicht = Pfund (Double) | Kg (Double)
umrechne :: Gewicht -> Pfund -> Double
umrechne Pfund (a) = (a*0.453 592 370)
umrechne :: Gewicht -> Kg -> Double
umrechne Kg (a) = (a/0.453 592 370)

Zum Seitenanfang    
 
Landei

Gepostet:
21.07.2012 19:59

   
Was genau sollen deine Signaturen bedeuten? Gewicht -> Pfund -> Double sind zwei Argumente. Einen Typ \"Pfund\" gibt es nicht, das ist ein Typ-Konstruktor. Der muss übrigens beim Pattern Matching geklammert werden: (Pfund a). Leerzeichen in Doubles sind auch nicht erlaubt.

Je nachdem, was du willst, gibt es mehrere Lösungswege. Mit deiner obigen data-Definition kannst du typmäßig nicht zwischen Pfund uns Kg unterscheiden. Möglich wäre eine gegenseitige Umrechnung, also jedes Pfund-Gewicht in ein Kg-Gewicht umzurechnen und umgekehrt, aber dieses Modell würde schon versagen, wenn eine dritte Gewichtseinheit dazukommt.

Besser ist es, Pfund und Kg separat zu definieren:


data Pfund = Pfund
data Kg = Kg

data Gewicht a = Gewicht Double a

pfund x = Gewicht x Pfund
kg x = Gewicht x Kg

pfundNachKg :: Gewicht Pfund -> Gewicht Kg
pfundNachKg (Gewicht x Pfund) = kg $ x * 0.453592370

kgNachPfund :: Gewicht Kg -> Gewicht Pfund
kgNachPfund (Gewicht x Kg) = pfund $ x / 0.453592370


Natürlich gibt es andere Lösungsmöglichkeiten (z.B. könnte Gewicht als Typklasse definiert werden, oder man könnte Typfamilien verwenden).
Zum Seitenanfang    
 
Landei

Gepostet:
21.07.2012 20:18

   
Das letzte Beispiel noch einmal aufgebohrt, so dass man nur eine Umrechnungs-Funktion braucht, und leicht weitere Einheiten hinzufügen kann:


data Pfund = Pfund deriving Show
data Kg = Kg deriving Show

class GewichtsEinheit a where nachKg :: a -> Double

instance GewichtsEinheit Pfund where nachKg Pfund = 0.453592370

instance GewichtsEinheit Kg where nachKg Kg = 1.0

data Gewicht a = Gewicht Double a deriving Show

nach :: (GewichtsEinheit a, GewichtsEinheit b) => Gewicht a -> b -> Gewicht b
nach (Gewicht x vonEinheit) nachEinheit =
Gewicht (x * (nachKg vonEinheit) / (nachKg nachEinheit)) nachEinheit

main = print $ (Gewicht 10 Kg) `nach` Pfund
--Gewicht 22.046226218487757 Pfund
Zum Seitenanfang    
 
IKT

Gepostet:
21.07.2012 20:51

   
Danke! Selber Klassen & Instanzen davon zuerstellen, damit habe ich mich bisher noch nicht beschäftigt (es gibt ja auch Leute auf #haskell die sagen, dass wenn man selber neue Klassen anlegt nur nicht lange darüber nachgedacht hätte, wie man das Problem sonst lösen könnte). Das sieht aber ziemlich cool aus, werd ich mir mal später näher anschauen.
Zum Seitenanfang    
 
Landei

Gepostet:
22.07.2012 12:52

   
Die prinzipielle Vorgehensweise ist inzwischen Allgemeingut, selbst wenn man sich Java-Bibliotheken anschaut (etwa JScience): Es hat sich herausgestellt, dass die Einheiten selbständig ansprechbar sein müssen, also nicht \"versteckt\" sein dürfen wie in Xans Entwurf. Davon ausgehend ist obige Version naheliegend - wobei gerade bei Maßeinheiten der Teufel im Detail steckt: Eine ernsthafte Implementierung müsste als zusätzliche Ebene noch Dimensionen und Basiseinheiten (was ich nur implizit über die Methode nachKg mache) berücksichtigen, eventuell auch \"Systeme\" (SI, imperial US, imperial UK).
Zum Seitenanfang    
 
Xan

Gepostet:
25.07.2012 11:37

   
Was bedeutet diese Dolarzeichen hier? kg $ x * 0.453592370
Zum Seitenanfang    
 
Landei

Gepostet:
25.07.2012 12:03

   
Im Beispiel oben könnte man genauso gut kg (x * 0.453592370) schreiben.

($) ist so definiert:


f $ x = f x


Also wird nur das Argument rechts an die Funktion links übergeben. Hört sich nicht besonders sinnvoll an, spart aber dank der Assoziativität von ($) oft Klammern (da alles rechts als Einheit angesehen wird), und ist bei mehreren Klammerebenen übersichtlicher.

Außerdem ermöglicht es Konstrukte wie...


map ($ 0.5) [sin,cos,tan,sqrt]
--[0.479425538604203,0.8775825618903728,0.5463024898437905,0.7071067811865476]


... die du ohne $ nicht schreiben könntest.
Zum Seitenanfang