![]() |
Maßeinheiten als Typen
Moinmoin,
da hier ![]() Dabei ist das ganze als Grundstock für ein größeres Bibleotheks-Projekt gedacht. Dabei gibts natürlich einige Anforderungen: 1. Die einzelnen Typen sollen sicher und eindeutig sein. Gemeint ist, das, wenn man das ganze als Parameter an eine Methode übergibt, keine andere Maßeinheit übergeben werden kann Beispiel:
Delphi-Quellcode:
Wenn versucht wird, hier Kilogram zu übergeben, soll der Compiler meckern.
Procedure TuWas(const a:gramm);
2. Rechenoperationen sollen möglich sein. D.h. Addition, Subtraktion, Division und Multiplikation 3. Vergleichsoperation sollen auch möglich sein. 4. Eine Möglichkeit, von einer Maßeinheit in eine andere Maßeinheit zu konvertieren/umzurechnen Wärend der Konzeptions und Tüftelphase (:)) bin ich auch auf das ein oder andere Problem gestoßen: a) Klassen funktionieren hier nicht Das größte Manko hier ist, das man keine Operatoren in Klassen definieren kann. Ein zweites, wenn auch weniger wichtiges Manko ist, das Klassen explizit erzeugt werden müssen. b) Records können nicht wirklich vererbt werden Ansonsten hätte man einen "Basistypen" geschaffen, und den Rest einfach davon abgeleitet, so das man das ganze mit weniger Aufwand umsetzen hätte können. c) Kein wirkliches forwarding bei Records Da man Records nur mit Zeigern forwarden kann, müssen einiges an Operatoren an ungewöhnlicher Stelle implementiert werden. D. h. dass zu Beispiel der Add-Operator für Gramm+Kilogram bei Kilogram implementiert wird, obwohl der Rückgabewert Gramm ist. Daher der Ansatz, für jede Maßeinheit einen entsprechenden Record schaffen. Wenn jemand einen alternativen Ansatz hat, immer her damit :) |
AW: Maßeinheiten als Typen
Dann erklär doch was die Problematik ist, wenn du ein Gewicht mit unterschiedlichen Einheiten übergibst.
Ich sehe da eigentlich kein Problem und bin daher der Meinung, dass ein Typ für Gewicht ausreicht und du keine Aufsplittung in Gramm, Kilogramm, Tonne Typen benötigst. |
AW: Maßeinheiten als Typen
Genaugenommen gibts 2:
1. Beispiel
Delphi-Quellcode:
das tut so nicht, da der Compiler meint es wären die gleichen Parameter.
Procedure Tuwas(const gramm:integer);overload;
Procedure Tuwas(const kilogram:integer);overload; 2. Beispiel
Delphi-Quellcode:
Hier könnte ich z.B. problemlos auch Tonnen übergeben, was u.U. zu recht...interresanten Problemen führen kann.
Procedure TuwasAnderes(const gramm:integer;const Kilogramm:integer);
Mit entsprechenden Typen kann ich sicherstellen, das dort, wo Gramm als Parameter erwartet wird, auch nur Gramm übergeben werden können. 3. Angenehmer Nebeneffekt
Delphi-Quellcode:
Würde ich hier z.B. einfach integer nehmen, könnte man das Ergebnis in die Tonne treten.
var
a : Gramm; b : Kilogramm; erg : Gramm; begin a := 10; b := 10; erg := a+b; //<-- Ergibt 10010 Gramm ! end; |
AW: Maßeinheiten als Typen
Das ist die Begründung dafür, warum du einen Typen für Gewicht benötigst, aber noch nicht die Begründung dafür, warum du mehrere Typen für Gewicht benötigst.
Delphi-Quellcode:
Und verwenden so
procedure Tuwas( const gewicht: TWeight );
begin ... end;
Delphi-Quellcode:
PS Als kleines Beispiel sei gennant, dass die Geschwindigkeit als Weg pro Zeit definiert ist und nicht als Meter pro Sekunde. Jede Geschwindigkeit kann auch als Meter pro Sekunde dargestellt werden und trotzdem gilt das auch wenn der Weg in Kilometer angegeben ist und die Zeit in Nanosekunden.
begin
Tuwas( TWeight.FromKilograms( 10 ) ); Tuwas( TWeight.FromGrams( 10 ) ); Tuwas( TWeight.FromTons( 10 ) ); end; |
AW: Maßeinheiten als Typen
und woher weis Tuwas, was er nun übergeben bekommen hat ? :)
Wenn die Methode nur mit bestimmte Einheiten arbeiten soll, hab ich hier nur die Möglichkeit, das über einen weiteren Parameter (nämlich die Einheit) und unterscheidet dann diese innerhalb der Methode. oder Es gibt für die entsprechenden Einheiten, entsprechende Überladungen. hmmmm... Natürlich könnte man in TWeight z.B. noch einen zusätzlichen wert für die Einheit mitführen. Aber letztlich würde das keine Vorteil bringen. Die Methoden und Operatoren müssten ja trotzdem die Einheiten unterscheiden. |
AW: Maßeinheiten als Typen
Zitat:
Das ergibt dann sowas wie
Delphi-Quellcode:
var
a:TWeight; b:TWeight; begin a.asKilogram = 10; b.asGramm = 10; TuWas(a+b); // 1010gramm werden übergeben end; |
AW: Maßeinheiten als Typen
Soweit schon richtig.
Und was machst du wenn Tuwas nur Gramm und Kilogramm verarbeiten soll nicht aber Tonnen oder Milligram ? Wie ich grad oben erweitert hab, kann Tuwas das ganze nicht unterscheiden. |
AW: Maßeinheiten als Typen
Hilft dir sowas?
Code:
Type
TKilogramm= type of Integer; TGramm = type of integer; ![]() |
AW: Maßeinheiten als Typen
Zitat:
Ich empfehle eine kurze Recherche bei Wikipedia zu Maßeinheiten, Vorsätzen und Einheitensystem zur Auffrischung. Möglich wäre auch, dass du dir mal anschaust wie das die Delphi RTL für System.TimeSpan.TTimeSpan regelt. Es ist mir beim Überfliegen dieses und des anderen Threads nicht ganz klar geworden, ob das mit den Gewicht nur ein Beispiel/Analogie ist oder du wirklich mit Gewichtsangaben arbeiten musst. Nehmen wir mal an, es geht wirklich um Gewicht, also du willst die physikalische Maßeinheit für die Masse in einer bestimmten Gravation beziffern: Gramm, Kilogramm, Tonne (1000 kg), Kilotonne (1000 * 1000 kg) sind keine verschiedenen Maßeinheiten. Es bezieht sich immer auf ein und dasselbe mit verschiedenen Vorsätzen für Maßeinheiten. Die SI-Basiseinheit für Masse ist das Kilogramm. Ein Gramm ist ein Tausendstel Kilogramm. Soweit bekannt, nix neues. Schaffe dir einfach einen einzigen konkreten Basisdatentyp, bspw. mit Gramm und gebe ihm die entsprechenden Methoden.
Delphi-Quellcode:
type
TKilogramm = type UInt64; TTonne = type UInt64; TKilogrammHelper = record helper for TKilogramm function ToString: string; end; TTonneHelper = record helper for TTonne function ToString: string; end; TGramm = type Double; TGewicht = record strict private FDasRichtigeGewichtInGrammAlsFliesskommazahl: TGramm; // Warum Fliesskomma? Vielleicht willst du auch mal Milli- und/oder Mikrogramm verarbeiten. // Man kann natürlich auch mit einen UInt32 oder UInt64 als Basis arbeiten, dann muss das aber im Vorfeld klar sein, welche die kleinste Größe ist. // Bei einen Kuchenrezept bspw. ist höchstwahrscheinlich die kleinste Gewichtsangabe sowas wie "eine Prise Salz/ein halbes Gramm". private // ggf. mit richtigen Gettern und Settern versehen property RawValue: TGramm read FDasRichtigeGewichtInGrammAlsFliesskommazahl write FDasRichtigeGewichtInGrammAlsFliesskommazahl; public class function Add(const A, B: TGewicht): TGewicht; static; function AsKilogramm: TKilogramm; // Hier kommen nur sinnvolle Werte größer null bei raus, wenn die interne Variable vor dem Komma mehr als 1000 groß ist. function AsTonne: TTonne; // Hier kommen nur sinnvolle Werte größer null bei raus, wenn die interne Variable vor dem Komma mehr als 1000 * 1000 groß ist. end; implementation { TGewicht } class function TGewicht.Add(const A, B: TGewicht): TGewicht; begin Result.RawValue := A.RawValue + B.RawValue; end; function TGewicht.AsKilogramm: TKilogramm; begin Result := Round(RawValue / 1000); end; function TGewicht.AsTonne: TTonne; begin Result := Round(RawValue / 1000 * 1000); end; { TKilogrammHelper } function TKilogrammHelper.ToString: string; begin end; { TTonneHelper } function TTonneHelper.ToString: string; begin end; |
AW: Maßeinheiten als Typen
Zitat:
:glaskugel: Aber du wirst deinen Grund haben. Interessieren würde es mich schon, weshalb z.B. nur "Tonnen" verwendet werden soll und alle anderen nicht. Gewicht ist Gewicht, egal welche Maßzahl verwendet wird. |
AW: Maßeinheiten als Typen
Zitat:
Wird eine Formel anders, wenn du statt mit 100kg einfach mit 0,1t rechnest? Oder würde die Formel nicht eher so aussehen
Delphi-Quellcode:
Oder um bei deinem Beispiel zu bleiben
class operator TDuration.Divide( const a: TLength; const b: TDuration ): TSpeed;
begin Result := TSpeed.FromMetersPerSecond( a.Meters / b.Seconds ); end;
Delphi-Quellcode:
Warum sollte es eine gesonderte Rolle spielen ob ich den Wert in Tonnen oder Kilogramm übergeben habe? In der Berechnung werden die korrekten Einheiten verwendet und nur das zählt.
function Tuwas( const gewichtA, gewichtB : TWeight ): Currency;
begin // Preis für A = 20 Euro pro Kilogramm // Preis für B = 30 Euro pro Gramm Result := 20 * gewichtA.Kilograms + 30 * gewichtB.Grams; end; |
AW: Maßeinheiten als Typen
hmm...ok..mit Type of macht es zumindest schon mal keine Probleme bei überladenen Methoden.
Aber wie bastel ich dann entsprechende Operatoren ? |
AW: Maßeinheiten als Typen
Ich würde sagen, Du machst es Dir zu schwer.
Bei uns gibt es die gleiche Problematik. Allerdings versuchen wir das nicht innerhalb der Verarbeitung abzufangen. Es gbt für jeden "Einheistfall" einen Basistypen. Bei uns für Länge, Flächen, Volumen, Gewicht, Winkel etc. Der Basistyp muss halt Deinen gesamten Wertebereich der jeweiligen Einheit abfangen. Umgewandelt wird nur zur Anzeige oder bei Lesen/Schreiben...... Das vereinfacht innerhalb des Programmes die ganze Rechnerei. Wir haben Edits die den Basistyp bekommen und je nach Einstellung das in der jeweiigen Einheit anzeigt und annnimmt. Alles einfach Testbar. |
AW: Maßeinheiten als Typen
Unter Verwendung der Units System.ConvUtils und System.StdConvs kann man da recht einfach was zaubern:
Delphi-Quellcode:
Natürlich kannst du dir auch selbst deine Einheiten und Konvertierungen erstellen, aber es gibt halt mit den oben genannten Units schon einen vorgefertigten Mechanismus dafür.
type
TGramm = type Double; TKilogramm = type Double; TWeight = record private FConv: TConvType; FValue: Double; public constructor Create(AValue: Double; AConv: TConvType); class operator Implicit(A: TWeight): TGramm; overload; class operator Implicit(A: TGramm): TWeight; overload; class operator Implicit(A: TWeight): TKilogramm; overload; class operator Implicit(A: TKiloGramm): TWeight; overload; property Conv: TConvType read FConv write FConv; property Value: Double read FValue write FValue; end; constructor TWeight.Create(AValue: Double; AConv: TConvType); begin FValue := AValue; FConv := AConv; end; class operator TWeight.Implicit(A: TWeight): TGramm; begin Result := Convert(A.Value, A.Conv, muGrams); end; class operator TWeight.Implicit(A: TGramm): TWeight; begin Result := TWeight.Create(A, muGrams); end; class operator TWeight.Implicit(A: TWeight): TKilogramm; begin Result := Convert(A.Value, A.Conv, muKilograms); end; class operator TWeight.Implicit(A: TKiloGramm): TWeight; begin Result := TWeight.Create(A, muKilograms); end; Das Konvertieren geht dann über ein Cast auf TWeight:
Delphi-Quellcode:
procedure TuWas(const Value: TGramm); overload;
var K: TKiloGramm; begin K := TWeight(Value); Writeln(Format('%1.3f g = %1.3f Kg', [Value, K])); end; procedure TuWas(const Value: TKiloGramm); overload; var K: TGramm; begin K := TWeight(Value); Writeln(Format('%1.3f Kg = %1.3f g', [Value, K])); end;
Delphi-Quellcode:
var
A: TKiloGramm; B: TGramm; C: TTonnen; begin A := 50; B := 1500; TuWas(A); TuWas(B); TuWas(C); // Fehlermeldung: Doppeldeutiger überladener Aufruf von 'TuWas' end; |
AW: Maßeinheiten als Typen
@Schokohase
Und wie stellst du sicher, das an Tuwas keine Tonnen übergeben werden, weil z.B. kein Preis dafür definiert ist ? @Uwe Naja...im Prinzip ists das gleiche, wenn ich alles in einen Typen reinschiebe. Außer das ich alles in einem Typen hab (der verdammt groß wird) hätte ich nicht wirklich was gewonnen. Die ganze Operator-Kombinationen müsst ich ja genauso implementieren und entsprechend testen. |
AW: Maßeinheiten als Typen
Zitat:
Wenn du davon 3 Trillionen Tonnen haben möchtest, dann lasse ich mir über den Typen das Gewicht in Kilogram geben und multipliziere das mit den 20 Euro oder ich lasse mir das über den Typen in Gramm geben und multipilziere das mit den 30 Euro (je nachdem). |
AW: Maßeinheiten als Typen
Ich glaub ich hab dich da schon richtig verstanden. Klar kann ich das umrechnen (von T in kg oder g). Wenn es aber keine T gibt (aus welchen Gründen auch immer), dann kann das Tuwas nicht unterscheiden.
|
AW: Maßeinheiten als Typen
Ich würde sagen, dass dann die Unterscheidung nicht mehr im Verantwortungsbereich von TuWas() liegt, sondern bereits vorher erledigt werden muss. Nämlich direkt nach der Eingabe in der Glue-Logic des Forms, weil das der einzige Zeitpunkt sein sollte, zu dem diese Unterscheidung Sinn ergibt. Vor allem weil ja vermutlich erst über die Beschriftung im Formular deutlich wird welche Einheit letztlich eingegeben wird. Die Geschäftslogik sollte das nicht mehr interessieren dürfen. Das wäre so meine Interpretation zumindest.
|
AW: Maßeinheiten als Typen
Zitat:
Ist es vielleicht so, dass es unterschiedliche Preise für kg und Tonnen gibt und du musst deshalb eine Unterscheidung machen. :glaskugel: Hat irgendwie etwas von dem ![]() |
AW: Maßeinheiten als Typen
Zitat:
Ansonsten nimmt man einen Typen für eine Einhait, dieser speichert den Wert so wie der als SI Einheit definiert ist (Gewicht-> Kilogramm, Länge->Meter, Temeraturdifferenz->Kelvin, usw.) und schafft sich einfache Zugriffsmöglichkeiten um diese dann beliebig umzurechnen. Also
Delphi-Quellcode:
ergibt dann die Zahl 1000000.
TWeight.FromTons(1).Grams
|
AW: Maßeinheiten als Typen
Zitat:
Anlehnend an die SI Einheiten, in der i.d.R. auch zusammengesetzte Formeln berechnet werden, heist das die Typen rechnen immer richtig. Lediglich bei der Ein- und Ausgabe will der "Mensch" halt gerne mal was anderes sehen :stupid: Rollo |
AW: Maßeinheiten als Typen
Zitat:
Delphi-Quellcode:
var
A: TKiloGramm; B: TGramm; begin A := 50; B := A; // compiliert ohne irgendwelche Konvertierungen, dun dun dunnn :( |
AW: Maßeinheiten als Typen
@Ghostwalker
Vielleicht solltest Du ein wenig ausholen und uns sagen was der Hintergrund Deiner Frage ist. Grundsätzlich würde ich mit einem Record arbeiten:
Delphi-Quellcode:
Gruß
Type
tEinheit=(mgr,gr,kg,ton); tMyrec=record Menge:integer; Einheit:tEinheit; end; K-H |
AW: Maßeinheiten als Typen
Zitat:
Zitat:
|
AW: Maßeinheiten als Typen
Das mit der direkten Zuweisung von verschiedenen Maßeinheiten ist eher Kontraproduktiv. Den dann wärs ja wieder möglich, bei Verwendung als Methoden-Parameter, bei Gramm, Kilogramm zu übergeben.
Das läst sich aber umgehen, in dem man statt Implizierter Typumwandlung die Explizite Variante wählt. Was mich allerdings irretiert ist, wo der Vorteil liegen soll, das ganze in einem Typ zu handhaben. @p80286 siehe 1 Post und Verweis auf anderen Thread :) Ursprünglich wollte ich für jede Maßeinheit (als Beispiel hab ich Gewichte genommen) einen eigenen eigenen Record definieren mit entsprechenden Operatoren und Convertierungen. Im anderen Thread ging es darum, ob man entsprechende Unittests generieren kann. Dabei kamm die Frage auf, ob das wirklich das beste Design ist (also einzelne Typen für Maßeinheiten), da es doch recht aufwendig ist. Da das ganze mit der Unittest-Frage nur indirekt zu tun hab, hab ich das ganze mal abgekapselt. |
AW: Maßeinheiten als Typen
Zitat:
Du musst nur verstehen das Kilogramm, Gramm, Mikrogramm, Nanogramm K E I N E verschiedenen Maßeinheiten sind. Es ist immer ein und dieselbe Maßeinheit mit verschiedenen Vorsätzen. Ich wiege gleichzeitig 100 kg = 100000 g = 100000000000000 ng. Das kann man mit einem einzigen Typen abbilden. |
AW: Maßeinheiten als Typen
Ich hab gestern zufällig einen Vortrag (Java, sorry) gehört über eine "JSR-354 Money and Currency API".
Grundprinzip war in etwa, das Geld jeweils aus einem Betrag und einer Währung besteht und alles jeweils Objekte sind, mit diversen Eigenschaften und Methoden. Dazu gibt es Umrechnungsobjekte von Währungen usw. Was ich damit sagen will: Wenn man es schon kompliziert und ausführlich machen will, mit viel Overhead, dann aber auch alles richtig als Objekte abgebildet mit Typsicherheit usw. (auch wenn Delphi nicht Java ist, kann man sich da ja was abgucken). |
AW: Maßeinheiten als Typen
Zitat:
Also Kg, ng, gr, m, cm, dm sind nur verschiedene Faktoren, das ist leicht. Aber es gibt auch Ausreisser, z.B. bei Temperatur (°C, °F, °K), das muss man mit Offset und Faktor arbeiten. Oder bei Winkeln und Längen in diversen Darstellungsformen, z.B. der worst case ist wohl in USA mit der grässlichen inch-feet Darstellung und Brüchen derselben. Rollo |
AW: Maßeinheiten als Typen
@Rollo62
Egal wie komplex die Umrechung ist, die Bedeutung bleibt aber immer dieselbe, egal in welcher Dimension ich diese Einheit darstelle/angebe. Es wird nicht heißer oder kälter wenn ich die Temperatur(-Differenz) in Celsius, Kelvin oder Fahrenheit angebe. Es wird nicht schwerer oder leichter wenn ich das Gewicht in Kilogramm, Tonnen oder Mikrogramm angebe. Der TE hat es aber bislang nicht geschafft auch nur ansatzweise eine Begründung zu liefern, warum er diese Aufsplittung einer Einheit in unterschiedliche Dimensions-Typen als nötig erachtet. Es kommt nur ein "ja, wenn man das mal braucht". |
AW: Maßeinheiten als Typen
Zitat:
Einzig Operatoren würde ich außen vor lassen. Addition und Subtraktion sind vllt. noch machbar, aber bei Multiplikation&Division wirds schwierig: Bspw. muss das Multiplikationsergebnis aus Spannung und Stromfluss vom Divisionsergebnis aus Drehmoment durch Zeit subtrahierbar sein. Guava macht das ganze übrigens ![]() |
AW: Maßeinheiten als Typen
Plot Twist: Das eigentliche Grundproblem ist etwas ganz anderes und kann mit drei Zeilen erschlagen werden! :twisted:
|
AW: Maßeinheiten als Typen
Natürlich ist es die gleiche Größe, das schreibe ich ja schon ganz unten.
Aber der Unterschied kann sein ob diese in einer Variablen gespeichert werden kann, oder ob ich mehrere brauche um alle Aspekte abzudecken. Wenn ich es nur z.B. in Double speichere können dabei Informationenen verloren gehen, wie z.B. beim Winkel welcher Quadrant, o.ä. Rollo |
AW: Maßeinheiten als Typen
@Rollo62
Bzgl. der Winkel und der Quadranten: Ein Winkel von 60° ist ein Winkel von 60°. Punkt. Genauso wie 1 Stunde auch 1 Stunde ist. Wenn du beim Winkel noch den Quadranten noch ins Spiel bringst, dann ist es das gleiche, als wenn du bei der Zeit noch die Information „nach Mitternacht in Wladiwostok“ ins Spiel bringst. Es ist dann eben nicht mehr einfach nur ein Winkel oder einfach nur eine Zeit. |
AW: Maßeinheiten als Typen
Ein Winkel von 60° im Gradmaß ist ein Winkel von 60° im Gradmaß. So wird ein Punkt draus.
|
AW: Maßeinheiten als Typen
Zitat:
Da will ich mich natürlich vielmals für entschuldigen. |
AW: Maßeinheiten als Typen
Es gibt Systeme die nur Winkel von 0 ... 90° liefern, wo man dann je nach Laune interpretieren kann in welchen Quadranten die liegen.
Ist auch egal, muss dich nicht überzeugen das es Grenzfälle gibt. |
AW: Maßeinheiten als Typen
Zitat:
Ein Winkelmesser kann auch nicht den Quadranten bestimmen und trotzdem gibt es einen Winkelmesser. Und das Ergebnis von dem Winkelmesser ist dann ein Winkel. Wenn der Quadrant benötigt wird (und ich bezweifle doch gar nicht, dass das auch benötigt werden kann), dann ist das eine zusätzliche räumliche Information zum Winkel die dann separat gespeichert werden muss, oder man macht sich dafür einen eigenen Typen, der dann Winkel und Quadrant beinhaltet. |
AW: Maßeinheiten als Typen
Zitat:
Du wirst mir aber sicher zustimmen, das es sehr wohl einen Unterschied macht, ob dir 100 g oder 100 t auf den Kopf fallen. @Jumpy Bin ich voll bei dir, was die Objekte betrifft, aber die mögen in Delphi nunmal keine Operatoren. Mal ganz davon abgesehen (das is aber rein eine persönlicher Faible), das man sie explizit erst erzeugen muss. |
AW: Maßeinheiten als Typen
@Ghostwalker
Diese Zusätze wie Milli-, Centi-, Dezi- oder auch Kilo- stehen ganz simpel für einfache Faktoren. Nicht mehr und nicht weniger. Es bleibt ein Gewicht. 1 Kilo-Gramm => 1 * 1000 Gramm => 1000 Gramm 0,1 Kilo-Gramm => 0,1 * 1000 Gramm => 100 Gramm Eigentlich sind 0,1kg und 100g gleich und du willst jetzt, dass 100g ein zulässiger Wert ist, aber 0,1kg ist nicht zulässig? Dann kann ich dir tatsächlich nicht mehr helfen, denn nun verlassen wir endgültig die rationale Basis für eine Diskussion. |
AW: Maßeinheiten als Typen
Zitat:
![]() ![]() ![]() Du unterscheidest beim Programmieren doch auch nicht, ob die Zahl Dezimal, Hexadezimal, Binär oder Octal dargestellt wird, oder? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:54 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz