読者です 読者をやめる 読者になる 読者になる

ScalaでApache Commons Mathを使う

Scalaで科学計算をしようとしたんだけど、Scalaデファクトスタンダードっぽいライブラリがない。
仕方ないので、Javaのライブラリを読み込むことにした。
JavaにはApache Commons Mathという良さげなライブラリがあったので、それを使うことにした。

Math - Download Apache Commons Math

上のリンクからBinariesを選んでダウンロードすればOK。

f:id:levelfour:20140719113144p:plain

アーカイブを解凍して、含まれているcommons-math-3.3.3.jar(バージョンは適宜読み替えて)が本体。
これを自分の作業ディレクトリにコピーする。
Javaのライブラリをシステムにインストールするどうのこうのという話は、僕にはわからない。
Pythonのpip、Rubyのgemみたいなパッケージ管理ツールが欲しいんだけれども)

で、適当にソースコードを書く。ドキュメントは割と充実してるっぽいので適当に参照。
【参考】Apache Commons Math 3.3 API

import org.apache.commons.math3.complex

object Main {
	def main(args: Array[String]) {
		val c = new complex.Complex(1,1)
		println(s"${c.sqrt().getReal}+${c.sqrt().getImaginary}i")
	}
}

Mathはorg.apache.commons.math3以下に入ってるので、必要そうなモジュールをimport。
僕の場合は複素数計算がしたかったので、org.apache.commons.math3.complex.Complexを利用。
(正直な話、モダンな言語なんだからこれくらい標準ライブラリに入れろよという感じはある)

サンプルソース\sqrt{1+i}を計算。

ところが。

実行方法がわからない。

コンパイル

$ fsc -cp commons-math3-3.3.jar Main.scala

どうやらcp(classpathの略)で必要なライブラリを指定してあげればコンパイルできるようだ。
ちなみに、fscはfast scala compilerなので、別に普通のscalacでもよい。

実行

$ scala Main
java.lang.ClassNotFoundException: org.apache.commons.math3.complex.Complex
	at java.net.URLClassLoader$1.run(URLClassLoader.java:372)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:360)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at Main$.main(Main.scala:5)
	at Main.main(Main.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:71)
	at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
	at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:139)
	at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:71)
	at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:139)
	at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:28)
	at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:45)
	at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:35)
	at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:45)
	at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:74)
	at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
	at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
	at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

Complexを使っているところでClassNotFoundExceptionを飛ばしているのだから、Commons Mathを動的リンクしているんだろう。
調べてみたところ、コンパイル時と同様classpathオプションで指定できるらしい。

もう一回実行

$ scala -cp commons-math3-3.3.jar Main
No such file or class on classpath: Main

???
今度はさっきコンパイルしたMainが読み込めない。どうしたものか。

結論

$ scala -cp ./:commons-math3-3.3.jar Main
1.09868411346781+0.45508986056222733i

つまり、classpathに自前でモジュールを指定すると、カレントディレクトリがclasspathから外れてしまうらしい。
そのため、カレントディレクトリも同時にclasspathに指定してやる必要があるとのこと。
ちなみに、複数のモジュール、パスを同時に指定する場合は上記のようにコロン区切りで入力する。

Scalaの情報が思った以上に少なくて、こういうのに苦労している。
コップ本嫁ってことですかそうですか