通常の分数の例としてのfreepascalでの演算子のオーバーロード

画像






私たちは皆、普通の分数が学校でどのように教えられたか覚えています。 分子、分母、NODおよびNOC、分数の算術演算。 しかし、実際の生活においても、法を含むさまざまな分野で通常の分数がうまく使用されています。たとえば、通常の分数では、事業会社の参加者の共有、共有共有権の共有などを表現できます。



したがって、通常の分数を処理するために、企業アプリケーションにいくつかの機能を何らかの形で実装する必要がありました。 デルファイであろうとフリーパスカルであろうと、Pascalの最新の実装はこのための便利なツールを提供します。



結果のモジュールは、オペレーターの過負荷を研究する過程で成長しました。

モジュールの準備が整ったとき、1年前、英語のfreepascalフォーラムの参加者が同様のフォーラムを開発しましたが、機能の大部分とそのさまざまな実装で全体的に重量を量ったことがわかりました。 まあ、それは一種の輸入代替をしたと考えます。


主なデータ型は次の構造になります。



TFraction = record Numerator: longint; Denumerator: longint; function Create(ANum, ADenum: longint): TFraction; function toStr: string; function toFloat: extended; end;
      
      





必要なコンパイラ指令を含めることを忘れないでください-{$ MODESWITCH ADVANCEDRECORDS}


構造には2つのフィールドがあります-整数分子と分母、1行に値を割り当てるコンストラクター関数、および通常の分数を文字列と小数に変換するための関数です。



モジュールの補助機能(独立して使用可能):



 //     procedure SetEqualDenum(var ALeftFr, ARightFr: TFraction); //   -     function ExpandFraction(AFraction: TFraction; Factor: longint): TFraction; //    function gcd(ALeftDenum, ARightDenum: longint): longint; //    function lcm(ALeftDenum, ARightDenum: longint): longint; //   -    ,  toGCD        function CollapseFraction(AFraction: TFraction; Divider: longint = toGCD): TFraction; //     function CompareFractions(ALeftFr, ARightFr: TFraction): TfrCompareResult; //   ,        function ReverseFraction(AFraction: TFraction): TFraction;
      
      





モジュールの主なものは、加算、減算、乗算、除算、割り当て、分数の比較のためのオーバーロードされた演算子です。

 //    operator +(ALeftFr, ARightFr: TFraction) r: TFraction; //     operator +(ALeftFr: TFraction; const Term: longint) r: TFraction; //   operator -(ALeftFr, ARightFr: TFraction) r: TFraction; //    operator -(ALeftFr: TFraction; const Sub: longint) r: TFraction; //    operator * (ALeftFr, ARightFr: TFraction) r: TFraction; //     operator * (AFraction: TFraction; const Multiplier: longint) r: TFraction; operator * (const Multiplier: longint; AFraction: TFraction) r: TFraction; //    operator / (ALeftFr, ARightFr: TFraction) r: TFraction; //     operator / (AFraction: TFraction; const Divider: longint) r: TFraction; //    operator = (ALeftFr, ARightFr: TFraction) r: boolean; // ,     operator > (ALeftFr, ARightFr: TFraction) r: boolean; // ,     operator < (ALeftFr, ARightFr: TFraction) r: boolean; //      ( = 1) operator := (const AIntegerPart: longint) r: TFraction; //    /   operator := (const AStringFr: string) r: TFraction;
      
      





残念ながら、freepascalでは、整数の列挙を割り当てられた値として転送することはできません(辞書、セット、好きなように呼び出しますが、これは不可能です:A:=(1,2); or B:= [1,2] )、したがって、分数の開始はコンストラクタ関数または文字列値を通過しますが、2つのフィールドの値を設定することを妨げるものは何もありませんが、できるだけシンプルにすることを望みました。


加算、除算、割り当て、比較などのオーバーロードメソッドの実装は、次のようになります。
 operator+(ALeftFr, ARightFr: TFraction)r: TFraction; begin SetEqualDenum(ALeftFr, ARightFr); r.Numerator := ALeftFr.Numerator + ARightFr.Numerator; r.Denumerator := ALeftFr.Denumerator; r := CollapseFraction(r, toGCD); end; ... operator/(ALeftFr, ARightFr: TFraction)r: TFraction; begin r := ALeftFr * ReverseFraction(ARightFr); end; ... operator:=(const AStringFr: string)r: TFraction; var i: integer; begin i := PosEx(char(SolidorSym), AStringFr); if not TryStrToInt(LeftStr(AStringFr, i - 1), r.Numerator) then raise Exception.Create('Numerator is not integer!'); if not TryStrToInt(RightStr(AStringFr, Length(AStringFr) - i), r.Denumerator) then raise Exception.Create('Denumerator is not integer!'); end; ... operator=(ALeftFr, ARightFr: TFraction)r: boolean; begin Result := CompareFractions(ALeftFr, ARightFr) = crEqual; end; operator>(ALeftFr, ARightFr: TFraction)r: boolean; begin Result := CompareFractions(ALeftFr, ARightFr) = crLeft; end; operator<(ALeftFr, ARightFr: TFraction)r: boolean; begin Result := CompareFractions(ALeftFr, ARightFr) = crRight; end;
      
      







繰り返しますが、「競合他社」モジュールには、より多くの関数とオーバーロードされた演算子があるため、さらにオーバーロードされます> =、<=、**、および小数点以下の割り当てと、「正しい」分数の出力を持つ文字列への変換も導入されました。する必要があります。



GCDを計算するには、最も単純な再帰アルゴリズムを選択しました。



 function gcd(ALeftDenum, ARightDenum: longint): longint; begin if ARightDenum = 0 then Result := abs(ALeftDenum) else Result := abs(gcd(ARightDenum, ALeftDenum mod ARightDenum)); end;
      
      





分数を減らしてNOCを計算するには、GCDが必要です。



 function lcm(ALeftDenum, ARightDenum: longint): longint; begin Result := abs(ALeftDenum * ARightDenum) div gcd(ALeftDenum, ARightDenum); end;
      
      





次に、NOCを使用して、分数を共通分母にします。



 procedure SetEqualDenum(var ALeftFr, ARightFr: TFraction); var tDenum: longint; begin if ALeftFr.Denumerator = ARightFr.Denumerator then exit; tDenum := lcm(ALeftFr.Denumerator, ARightFr.Denumerator); ALeftFr := ExpandFraction(ALeftFr, tDenum div ALeftFr.Denumerator); ARightFr := ExpandFraction(ARightFr, tDenum div ARightFr.Denumerator); end;
      
      





そして、この関数は、加算、減算、比較のオーバーロード演算子で結果として使用されます。



オーバーロード演算子を使用すると、次のような単純な割り当てを記述できます。



 Fr1, Fr2: TFraction; ... Fr1 := 12; // (  12/1) Fr2 := '3/5'; // (   ) //     Fr3 := TFraction.Create(22,7); // 22/7
      
      





分数と不等式を書き留めやすくなります:



 Fr3 := Fr1+ Fr2; Fr3 := Fr1 * Fr2; Fr2 := Fr1 - 1; Fr2 := Fr1 / 3; Fr3 := Fr1 / Fr2; if Fr1 > Fr2 …
      
      





結合代入演算子も機能します:



 Fr1 += Fr2; Fr2 -= 1; Fr3 *= '1/2';
      
      





次の式でも使用できます。



 if Fr1 > '2/3' ... while Fr2 < 1 ...
      
      





これらの不等式は完全にコンパイルされ、正しい論理結果が得られます。



標準のfreepascalパッケージには、数学的な行列と複素数を扱うための同様のモジュールがいくつかあります。それらの実装を例として見ることができます。

私の意見では、演算子のオーバーロードと除算方法によるオーバーロードを行わない場合、演算子のオーバーロードは、ジェネリック(特にfglのリストが特に好きです)と同じように、コードの簡潔さと視覚化を簡素化します。 (データ型の不一致が原因で)演算子が過負荷になっていることを忘れると、コンパイラは常に(ほとんど)停止します。



説明したモジュールは完璧とはほど遠いです。数学演算中にゼロを取得し、ゼロで除算する場合、結果の処理を追加する必要があります。また、通常の分数に小数を追加することができます。



モジュールの全文はここにあります

freepascal.orgフォーラムのモジュール



All Articles