倭算数理研究所

科学・数学・学習関連の記事を、「倭マン日記」とは別に書いていくのだ!

倭マン's Math (3) : DoubleArray (org.apache.commons.math.util パッケージ)

今回は org.apache.commons.math.util.DoubleArray インターフェスにメソッドを追加していきます(一覧)。 このインターフェースは基本的に double の配列ですが、要素にアクセスするメソッドが最低限しか定義されていないのでかなり使いづらいのが正直なところ。 この記事では、GDK によってオブジェクトや double の配列に追加されているメソッドを参考に、あれこれメソッドを定義してみました。

  • インスタンスの生成
  • 要素へのアクセス
  • 等値評価 equals()
  • 型変換 asType()
  • その他のメソッド

インスタンスの生成


まずはインスタンスの生成に関連するメソッド。 DoubleArray を ResizableDoubleArray のコンストラクタから生成するのは少々面倒なので(空の DoubleArray を作って要素を詰める何てもってのほか!)、List や double からインスタンスを取得できるようにしておきましょう。 このためには DoubleArray インターフェースではなく、 List や double のメタクラスをいじらないといけませんねぇ:

        List.metaClass.doubleArray = {
            def result = new ResizableDoubleArray(size())
            delegate.each{ result << it }
            return result
        }
        
        double[].metaClass.doubleArray = {
            return new ResizableDoubleArray(delegate)
        }

使い方は、List や double[] のオブジェクトに対して doubleValue() メソッドを呼び出すだけです:

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

WamansMath.enhance(CommonsMathPackage.UTIL)

// List から生成
def array1 = [0d, 1d].doubleArray()
// double[] から生成
def array2 = ([0d, 1d, 2d] as double[]).doubleArray()

まぁ、簡単!

要素へのアクセス


次は要素へのアクセスを行うメソッド。 とりあえず、要素の個数を取得するメソッド size() は必須。 また、配列なら getAt() / putAt() もいりますね。 他に、要素を追加する << や、Groovy では使わずにはいられない each, collect なども定義してみました:

        DoubleArray.metaClass.define{
            ...

            size = { -> getNumElements() }
            getAt = { int i -> getElement(i) }
            putAt = { int i, double d -> setElement(i, d) }

            leftShift = { Number value ->
                addElement(value.doubleValue())
                return delegate
            }

            each = { Closure c ->
                for(double d in getElements()) c.call(d)
            }

            collect = { Closure c ->
                def result = []
                for(double d in getElements()) result << c.call(d)
                return result
            }
        }

ホントなら find() / findAll() や any() / every() なども定義した方がいいかもしれませんが、キリがないのでこの辺で。 続きは「その他のメソッド」にて。 定義したメソッドの使い方は特に問題ないかと:

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

WamansMath.enhance(CommonsMathPackage.UTIL)

def array = new ResizableDoubleArray()

// << (leftShift()) で要素の追加
0.upto(3){ array << it }

// size() で要素の個数を取得
assert array.size() == 4

// each, collect
array.each{ assert it instanceof Double }
assert array.collect{ it*2d } == [0d, 2d, 4d, 6d]

// getAt() / setAt() で要素へアクセス
assert array[2] == 2d
array[2] = 4d
assert array == [0d, 1d, 4d, 3d]

等値評価 equals()


GDK では List と配列などの等値評価も、キチンと要素の値で行われるようになっていますよね。 なので DoubleArray も List や double[] と要素の値で等値評価されるようにしておきましょう:

        DoubleArray.metaClass.define{
            ...

            equals = { List args -> return getElements() == args }
            equals = { double[] args -> return getElements() == args }
        }

使い方:

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

WamansMath.enhance(CommonsMathPackage.UTIL)

def array = [0d, 1d, 2d, 3d].doubleArray()

// List と等値評価
assert array == [0d, 1d, 2d, 3d]
// double[] と等値評価
assert array == ([0d, 1d, 2d, 3d] as double[])

型変換 asType(Class)


型変換も List と double[] を想定して実装しておきましょう:

        DoubleArray.metaClass.define{
            ...

            asType = { Class type ->
                switch(type){
                    case DoubleArray.class:
                        return this
                    case List.class:
                        return Arrays.asList(getElements())
                    case double[].class:
                        return getElements()
                    default:
                        throw new ClassCastException(type.name)
                }
            }
        }

使い方:

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

WamansMath.enhance(CommonsMathPackage.UTIL)

def array = [0d, 1d, 2d, 3d].doubleArray()

// List へ変換
assert (array as List) == [0d, 1d, 2d, 3d]
// double[] へ変換
assert (array as double[]) == ([0d, 1d, 2d, 3d] as double[])

その他のメソッド


GDK で定義されている配列や List のメソッドを全て DoubleArray に追加するのは面倒なので、ちょっと楽をして「メソッドがないなら、getElements() で返される double オブジェクトに処理を委譲する」ようにしておきましょう。 これには methodMissing を定義しておけば OK です。 いくつかうまくいかないメソッド(返り値が通常の配列 double になる)もあるようですけど、まぁあんまり気にしないでおこう(笑)

        DoubleArray.metaClass.define{
            ...

            methodMissing = { String name, args ->
                getElements().invokeMethod(name, args)
            }
        }

サンプルは省略。 これにて DoubleArray の拡張完了。

Javaによるアルゴリズム事典

Javaによるアルゴリズム事典