Groovy で複素数の計算処理を実装する。
最終更新:2014/09/06
calc.groovy 複素数の計算式を処理するスクリプト// calc.groovy // Version 1.0 // KAKU PROJECT (2014) // 概要:複素数の計算式を処理するスクリプト // 実行例: groovy calc.groovy file // ビルトイン定数とビルトイン関数の定義 def binding = new Binding([ // i: 虚数 i: new Complex(0, 1), // Re: 複素数から実数部を取り出す関数 Re: {z -> z.r}, // Im: 複素数から虚数部を取り出す関数 Im: {z -> z.i}, // conjugate: 複素数の共役複素数を作る関数 conjugate: {z -> def z2 = new Complex(z) new Complex(z2.r, -z2.i)}, // conjugate: 複素数の絶対値を計算する関数 abs: {z -> def z2 = new Complex(z) java.lang.Math.sqrt(z2.r**2 + z2.i**2)} ]) // Number クラスに複素数オブジェクト用の演算を追加する。 // 加算 Number.metaClass.plus = {Complex b -> new Complex (delegate, 0)+b } // 減算 Number.metaClass.minus = {Complex b -> new Complex (delegate, 0)-b } // 乗算 Number.metaClass.multiply = {Complex b -> new Complex (delegate * b.r, delegate * b.i) } // 除算 Number.metaClass.div = {Complex b -> new Complex (delegate, 0)/b } // 入力ファイルの各行の式を加工し、ストリングバッファに保存。 def sb = new StringBuffer() new File(args[0]).eachLine { sb << "println (\"${it}\\n==>\${${it}}\\n\")\n" // println sb } // メモ: // 入力行→xxx // 評価される行→println ("xxx\n==>${xxx}\n") // エスケープした行→"println (\"xxx\\n==>\${xxx}\\n\")\n" // 式の評価。 def shell = new GroovyShell(binding) shell.evaluate( sb.toString() )Complex.groovy 複素数のクラス。
// Complex.groovy // Version 1.0 // KAKU PROJECT (2014) // 概要:複素数を表現するクラス public class Complex { // ○複素数の実数部・虚数部プロパティ // ・実数部 private Number r = 0 // ・虚数部 private Number i = 0 // メモ:実数部・虚数部の値は Number クラスにしておくことで、任意の数値クラス // (Integer,Long,Double,float,BigDecimal)を使えるようにする。 // ・実数部の参照メソッド public Number getR () {r} // ・虚数部の参照メソッド public Number getI () {i} // メモ:変更メソッド(setR, setI) は用意しない。 // ○コンストラクタ // ・実数部のみの複素数 public Complex (Number r) { this.r = r this.i = 0 } // ・実数部+虚数部からなる複素数 public Complex (Number r, Number i) { this.r = r this.i = i } // 既存の複素数オブジェクト用コンストラクタ public Complex (Complex z) { this.r = z.r this.i = z.i } // ○演算子用メソッド。 // メモ:各演算子の結果は常に新しいオブジェクトを作成し、既存のオブジェクトは // 変化させないこととする。++ や -- などの単項演算子は用意しない。 // ・加算(二項演算・引数:複素数) public Complex plus(Complex b) { new Complex(r+b.r, i+b.i) } // ・加算(二項演算・引数:実数) public Complex plus(Number n) { new Complex(r+n, i) } // ・減算(二項演算・引数:複素数) public Complex minus (Complex b) { new Complex(r-b.r, i-b.i) } // ・減算(二項演算・引数:実数) public Complex minus(Number n) { new Complex(r-n, i) } // ・乗算(二項演算・引数:複素数) public Complex multiply (Complex b) { new Complex((r*b.r)-(i*b.i), (r*b.i)+(i*b.r)) } // ・乗算(二項演算・引数:実数) public Complex multiply(Number n) { new Complex(r*n, i*n) } // ・除算(二項演算・引数:複素数) public Complex div (Complex b) { if (b.isZero()) { // ゼロ除算例外 throw new java.lang.ArithmeticException("Division by zero") } if (this.equals(b)) { // 同じ複素数なら1を返す。 return new Complex(1, 0) } double t = b.r*b.r + b.i*b.i new Complex( (r*b.r+i*b.i)/t, (i*b.r-r*b.i)/t) } // ・除算(二項演算・引数:実数) public Complex div (Number n) { if (n == 0) { // ゼロ除算例外 throw new java.lang.ArithmeticException("Division by zero") } new Complex(r/n, i/n) } // ・累乗(二項演算・引数:整数) public Complex power (int n) { //println "n:$n" Complex z = this if (n < 0) { z = new Complex(1, 0).div(this.power(-n)) } else if (n == 0) { z = new Complex(1, 0) } else { z = this for (int i=1; i<n; i++) { //println "i:$i" z = z.multiply(this) //println "z:$z" } } z } // ・マイナス(単項演算) public Complex negative () { new Complex(-r, -i) } // ・プラス(単項演算) public Complex positive () { this } // ・比較(二項演算・引数:複素数) public boolean equals (Complex b) { this.r == b.r && this.i == b.i } // ・補助メソッド。0 か否かを調べる public boolean isZero () { this.r == 0 && this.i == 0 } // 文字列化(toString) // ・補助メソッド。虚数部の文字列化。プラス符号なし。 private String toImgString2 () { if (i == 0) { "" } else if (i == 1) { "i" } else if (i == -1) { "-i" } else if (i < 0) { "-${i*(-1)}i" } else { "${i}i" } } // ・補助メソッド。虚数部の文字列化。プラス符号あり。 private String toImgString1 () { if (i == 0) { "" } else if (i == 1) { "+i" } else if (i == -1) { "-i" } else if (i < 0) { "-${(double)i*(-1)}i" } else { "+${(double)i}i" } } // ・複素数の文字列化 public String toString () { if (i == 0) { if (r == 0) { "0" } else { "${(double)r}" } } else { if (r == 0) { toImgString2() } else { "${(double)r}${toImgString1()}" } } } } // End of class Complex複素数の計算式のファイル例。
3+4*i 4.0+3.0*i (3+4*i)*(4.0+3.0*i) (3+4*i)*(4.0+3.0*i)*i (3+4*i)/(4.0+3.0*i) (0.96+0.28*i)*(4.0+3.0*i)実行例
C:\work\complex>groovy calc.groovy z.txt 3+4*i ==>3.0+4.0i 4.0+3.0*i ==>4.0+3.0i (3+4*i)*(4.0+3.0*i) ==>25.0i (3+4*i)*(4.0+3.0*i)*i ==>-25.0 (3+4*i)/(4.0+3.0*i) ==>0.96+0.28i (0.96+0.28*i)*(4.0+3.0*i) ==>3.0+4.0i
●メモ。演算子のオーバーロードについて。
(0.96+0.28*i)*(4.0+3.0*i)の式において、各演算子は次のように展開される:
(0.96.plus(0.28.multiply(i)).multiply(4.0.plus(3.0.multiply(i)))この際に各演算子の優先順位は Groovy の処理系が決定する。 例えば、0.96 への plus メソッドの適用時には、calc.groovy の 27~28 で Number クラスに追加したメソッドが使われる。 また、"i" は calc.groovy の 10 行目で定義されたビルトイン定数が使われる。
KAKU PROJECT (2014) |