DEVELOPERS BLOGデベロッパーズブログ
最近子供の宿題で、分数の計算をする機会が増えた。
正解例はもらえないので、答え合わせをするにも50問も100問もあると親が実際に計算をすることになり少々 (かなり) 大変だ。
ということで、簡単な分数電卓を CakePHP で Web ページ上に実装してみた。(実装例はこちら。)
基本構想
- 電卓ページの実装はフォームのテキスト入力欄に分数と四則演算を入力し、ボタンクリックで結果を表示する。
- 分数の表記は入力の容易さと構文解析の容易さのバランスを取り、帯分数 $$1 \frac{\;2\;}{\;3\;}$$ (1と3分の2、古い読み方なら 1荷(か)3分の2) なら [1&2/3]、帯分数でない分数 $$\frac{\;17\;}{\;23\;}$$ (23分の17) なら [17/23] のようにする。
- 結果は「テキスト」ではなく、MathJax を使用して数式として見やすい形式で出力する。
必要なもの
分数電卓を実装するためには、以下のものが必要となる。
- 分数のデータの保持および演算機能を提供するクラス (Fraction クラス)
- 入力した分数式を解釈し演算順序を判定するための構文パーサ
今回は Fraction クラスについて解説する。
PHP には分数に対応する型は存在していないので、クラスでデータ保持構造と演算機能を実現する。クラス構造としては、分母と分子だけからなる BaseFraction クラスをまず定義する。これは、分子・分母の2つの要素だけで演算することにより四則演算の実装が容易なためだ。そして BaseFraction クラスを拡張する形で帯分数に対応した Fraction クラスを実装する。
BaseFraction クラス
まずはコア機能を実装するための BaseFraction の説明から。
主なプロパティ (メンバー変数)
- protected $sign
-
分数全体の符号を表す整数値をとる。-1 ⇒ 負、0 ⇒ ゼロ、1 ⇒ 正。「分数が 0 (ゼロ) である」とは、分子 (および分母) の値の如何に関わらず「$sign が 0 であること」である。
- protected $numerator
-
分子。値は GMP (Gnu Multiple Precision) 数で実装することで、多倍長整数を扱う。
- protected $denominator
-
分母。値は GMP 数で実装し、多倍長整数を扱う。なお 0 (ゼロ) を指定した場合は例外 Exception をスローする。
主なメソッド
- public function __construct($num, $den=1)
-
コンストラクタ。分母($den) を省略した場合は 1。なお、与えられた分母と分子は約分せずそのまま設定する。約分が必要な場合は、パブリックメソッド reduction() を使用する。
- protected function copy()
-
当該オブジェクトのコピーを作成して返す。
- public function getSign()
-
BaseFraction の符号情報 (プロパティ $sign の値) を返す。
- public function getDenominator()
-
分子の値 (GMP 数) を取得する。
- public function getNumerator()
-
分母の値 (GMP 数) を取得する。
- public function reduction()
-
分数を約分し、当該オブジェクト自身を返す。
- public function add($f)
-
引数 $f で与えられる BaseFraction オブジェクトを、当該オブジェクトに加算する。結果は既約分数。
- public function mul($f)
-
引数 $f で与えられる BaseFraction オブジェクトを、当該オブジェクトに掛ける。結果は既約分数。
- public function invert()
-
当該オブジェクトの逆数を BaseFraction オブジェクトとして返す。なお、当該オブジェクトの分子が 0 の場合は例外 Exception を発生する。
- public function neg()
-
当該オブジェクトのコピーを作成し、符号を反転して返す。
- public static function is_base_fraction($o)
-
引数 $o が BaseFraction のインスタンスであるかどうかを調べる。
Fraction クラス
続いて BaseFraction を拡張した Fraction を定義する。
主なプロパティ
Fraction クラスでは、プロパティは定義しない。親クラス BaseFraction のプロパティを継承。
主なメソッド
- public function __construct($int, $num=0, $den=1, $sign=NULL)
-
コンストラクタ。$int は帯分数の整数部、$num は分子、$den は分母、$sign は分数全体の符号を指定する。
なお、実際には帯分数を仮分数に変換し、その仮分数で BaseFraction オブジェクトを生成する。
- public function getIntegralPart()
-
Fraction オブジェクトの整数部分を返す。実際には整数部はデータとしては保持していないので、分子と分母から毎回計算する。
- public function getNumerator($raw=FALSE)
-
分子を取得する。パラメータ $raw が TRUE の場合は仮分数の分子を、そうでない場合は帯分数化した場合の分子を計算して返す。
- public static function is_fraction($o, $strict=FALSE)
-
与えられたオブジェクト $o が分数かどうかを判定する。
第2引数 $strict が FALSE (既定値) の場合、分子が 0 の場合は「整数」とみなし分数とはしない。
- public function is_zero()
-
当該分数オブジェクトが 0 (ゼロ) かどうかを判定する。
- public function is_positive()
-
当該分数オブジェクトが正かどうかを判定する。
- public function is_negative()
-
当該分数オブジェクトが負かどうかを判定する。
- public function add($f)
-
引数 $f を当該オブジェクトに加算する。結果は既約分数。
- public function mul($f)
-
引数 $f を当該オブジェクトに掛ける。結果は既約分数。
- public function neg()
-
当該オブジェクトの複製を作成し、符号を反転させたものを返す。
次回は分数演算の式パーサを解説する予定。
この記事は加藤 正人さんが書いています!
- 氏名
- 加藤 正人
- 役職
- 多分SE
- 血液型
- 秘密
- 出没
- 美味しいもののあるところ
- 特色
- タヒチ大好き。ちょいメタボ。
- 2020/12/15
- CakePHP3 のリダイレクトがうまく行かない例
- 2020/11/12
- Apache Bench
- 2020/10/05
- PhpSpreadsheet でExcel ファイルを読み込む (CakePHP3)
関連エントリー
- 2015/04/11
- 分数電卓を作ってみる・その2
2020/12/15
2020/11/12
2020/10/05
2020/09/11
2020/08/03