goo

Javaの例をScalaに変換して説明してみる

Javaのオブジェクト指向っぽい説明の例を、Scalaに書き換えてScalaを説明してみる。
Scalaはオブジェクト指向と関数型が融合された言語なので、ベースとしてJavaと似たオブジェクト指向(クラス)が使える。


Javaのインターフェースは、Scalaではトレイトで書く。

trait 政党 {
  def 政策を作成する() : 政策
}

defでメソッドを定義する。(Scalaは関数型言語でもあるが、クラスやトレイト内に定義するのは関数ではなくメソッド。場面に応じて、メソッドは関数に(自動的に)変換して使われる)
Javaとは違い(Pascalと似て)、メソッドの戻り値の型(や変数の型)はコロンで区切って後ろに書く。
抽象メソッドの宣言はabstractキーワードをつける必要は無く、メソッド本体を記述しないだけ。
また、可視性は、何も書かない場合はpublicになる。

世界に一つしか無い官僚機構は以下のような感じで書ける。

object 官僚 {
  def 政策を実施する(s: 政党) : 結果 = {
    try {
      val dummy =  s.政策を作成する()
    } catch {
      case e: Exception =>
    }
    new 天下り先()
  }
}

(トップレベルに書かれた)objectは、シングルトンオブジェクトとなる。つまりnewしなくても自動的にVM内に1つだけインスタンスが作られる。
(トップレベルでなくクラス内に書かれたobjectはクラスをインスタンス化する際にそのインスタンス毎に作られる(staticの代わりではない)ので注意)

メソッド本体は、戻り型の指定の後ろに「=」を付けてその後ろに書く。
各文の末尾のセミコロンは省略可能。Scalaでは普通は書かない。
メソッドの戻り値としては、波括弧でくくられたブロックの一番最後の値が返る。したがって「return」は書かない。(Javaと違って、メソッド内の途中でreturnで抜けることは基本的にしない)

try〜catchの機能はJavaと同じだが、catch句の書き方がちょっと違う。(Scalaのmatch式の書き方に近い)
caseの「=>」の後ろに例外処理を書くのだが、官僚は握りつぶしている(何も処理しない)ので、例としては良くないな^^;


トレイトをミックスイン(「インターフェースを実装する」のと同じような意味)してクラスを書くには以下の様にする。

class 自党 extends 政党 {
  override def 政策を作成する() : 政策 = new 我田引鉄()
}

class 共党 extends 政党 {
  override def 政策を作成する() : 政策 = new 実現不可能な理想論()
}

メソッドをオーバーライドする際はoverrideを付ける。Javaでは@Overrideアノテーションだったが、Scalaでは言語内で規定されたキーワードとなっている。
(抽象メソッドをオーバーライドする際はoverrideを付けなくてもいい(付けないのが多数派だ)が、個人的には付けた方が良いと思っている。オーバーライドしていることが一目瞭然だし、親クラスのメソッド名を変えたときにコンパイルエラーになってくれるし)

メソッド本体が1つの式で収まる場合は、波括弧のブロックにする必要が無い。
(Scalaの構文としては、メソッド本体は1つの式を指定するだけ。複数の文を書きたい場合に波括弧のブロックを利用するという扱い)

インスタンスをnewで作ることはJavaと同じ。
ただし、コンストラクターの引数が無い場合は丸括弧も省略できる。

val 元祖自党 = new 自党()
var 亀下新党 = new 自党
var 石井新党 = new 自党
var 橋原新党 = new 自党

変数はvarまたはvalで定義する。
valは、Javaでのfinal変数と同じ。つまり、valで定義した変数には他の値を再代入することが出来ない。変数が別のインスタンスを指すことが出来ないというだけで、変数内のインスタンスの中の値が書き換えられないわけではない。Scala(関数型の考え方?)としては、インスタンス内が書き換えられないようにクラス(イミュータブルなクラス)を作るのが望ましい。

イミュータブルなクラスでは、インスタンス内の一部を書き換えたい場合は、書き換えずに新しいインスタンスを生成する形となる。
今回の政党の機能としては扱っていないが、政党間で党員の移動(自党から民党への移籍等)は非常に頻繁に起こるので、インスタンス内の党員の一覧は書き換え可能な方がやりやすいだろう。
(個人的には、現在のScalaがJavaVM上で動いている以上はインスタンスを大量に生成するのは効率が悪いので、大量に書き換えが発生しそうな用途ならミュータブルにした方がいいと思う。ぜひ、Scalaにはイミュータブルでも効率の良いScalaVMを作って欲しいw)

あと、自分は手続き型言語歴が長いので、変数へ「代入する」という言葉を使うが、Scalaでは「束縛する(bind?)」と言うのが正しいっぽい。
変数名やメソッド名などに値を束縛する(割り当てる・固定する)というニュアンスなんだと思う。


既存のクラスを継承したクラスは以下の様に書く。

class 民党 extends 自党

独自のメソッドやフィールドを定義しない場合は、クラス定義の波括弧が省略できる。


コンストラクターの引数で渡された値をフィールドに保持するだけの場合、クラス名の直後に引数(フィールド定義を兼ねる)を指定するような書き方が出来る。

class 委譲政党(protected val s: 政党) extends 政党 {
  override def 政策を作成する() = s.政策を作成する() //他の政党に委譲する
}

val 公党 = new 委譲政党(元祖自党)
val 社党 = new 委譲政党(元祖自党)

メソッドの戻り値の型がメソッド本体から推測できる場合は、defの型の指定は省略することが出来る。

ちなみに、Javaと同じような書き方をすると、以下の様にくどくなる。

class 委譲政党 extends 政党 {
  protected var s:政党 = null
  def this(s: 政党) = {
    this()
    this.s = s
  }
  override def 政策を作成する() = s.政策を作成する() //他の政党に委譲する
}

Scalaのクラスでは基本コンストラクターというものが必ず出来る。クラス名の直後に引数を書くとそれが基本コンストラクターの引数となる(省略された場合は引数なしのコンストラクターとなる)。
それ以外のコンストラクター(補助コンストラクター)はthisという名前のメソッドを定義するような書き方をする。
上記のthisメソッドでは、先頭でthis()を呼び出しているが。def thisで作った補助コンストラクターは必ず(自分より前に定義された)他のコンストラクターを呼び出す必要がある。ここら辺はJavaのコンストラクターとは考え方が違うところ。


基本的にはJavaのオブジェクト指向で出来る事はScalaでも出来るが、Scala独自の考え方で異なっている部分もある。

Scalaの勉強としては、次のステップはList操作(ScalaではSeqというクラス)を勉強するのが良いと思う。関数型を生かした便利メソッドがいっぱい出てくるがw、foreachが基本でfiltermap辺りがまず重要。

コメント ( 0 ) | Trackback ( 0 )

Play2.0 Eclipse設定作成時にソースを添付する方法

ueshinさんがTwitterでeclipsifyにwith-source=trueという便利なものがあるとつぶやいていたので、ほほう!と思って試してみたら、上手くいかなかった(爆)

色々教えていただいた結果、とりあえず何とかする手順は分かったので、メモ。

…それにしても、こんなにハマるとは思わなかったなぁ(苦笑)

コメント ( 0 ) | Trackback ( 0 )

ScalaでXMLファイルを読み込む方法

ScalaXMLファイルを読み込むにはXMLオブジェクトのloadFileメソッドを使えばいい。のは以前から一応知っていたが、この方法ではルート要素は読めるけど処理命令は読めない。
デバッグモードでステップ実行しつつ見てみたところ、XMLオブジェクトの継承元であるXMLLoaderを直接使えば処理命令も読めることが分かった。
XMLファイル内のコメントを取得するには、さらにハンドラーとかを用意しなければならないようだけど、これも一応出来た。 

ところで、XMLファイルを読み込むには、ConstructingParserを使う方法もある。
こっちだとファイル内に指定されているエンコーディングも取れるし、DTD(DOCTYPE)も取れる。特に何もしなくても処理命令やコメントも取れる。
遥かに簡単じゃん!なんだよちくしょーw

コメント ( 1 ) | Trackback ( 0 )

1から「10のn乗」までの合計を算出するプログラム

Hadoopで1〜100の合計を算出するプログラムを考えてみた訳だが、冷静に考え直すまでもなく、Hadoopの出番じゃないんだよな(爆)
しかし100程度だからいけないのであって、もっと桁数が大きくなればHadoop向きのサイズになるんじゃないか? intの上限(Javaなら32bit)を超える程度だと「longを使えばー?」と言われてしまうので、longは十進数だと18桁まで入るから、19桁の数、つまり「1から10の19乗」までの合計を出すなら、いける!?
JavaならBigIntegerというクラスがあるので、メモリーの許す限りの桁数の整数は扱えるし。

とは言え、1つのMapTaskで10億回(10の9乗)の加算をやるとしても、10の10乗(つまり100億)個のMapTaskが必要となる計算。1000ノードのHadoopクラスターで、1ノード当たり1千万回のタスクを実行か。10コアのCPUだとして、1コア当たり百万タスク。
やっぱり現実的じゃないかもなー(汗)


同じく現実的ではないが、Scalaなら、BigIntegerに相当するBigIntというクラスがあるので、Intでのプログラムと同様にシンプルに書ける。

//Int版
def f0(n: Int) = (1 to math.pow(10, n).toInt).par.sum

//BigInt版

def f1(n: Int) = (BigInt(1) to BigInt("1" + "0"*n)).par.sum

BigIntには初期値としてStringを渡せる。"0"*nは、"0"をn個並べた文字列になるので、10のn乗を作れる。これでばっちり!
と思いきや、toメソッドにはIntの限界があるようで、エラーになってしまった…。

scala> f1(19)
java.lang.IllegalArgumentException: 1 to 10000000000000000000 by 1: seqs cannot contain more than Int.MaxValue elements.

ちなみに、もっと少ない数でもOutOfMemoryになったよ。何故だー?orz

scala> f1(7)
java.lang.OutOfMemoryError: Java heap space


仕方ないので等差数列の和の公式にしてみたら、ちゃんと出来た。
BigIntはIntと同じく*や+が使えるので、コーディングもしやすい。 

def f2(n: Int) = { val m = BigInt("1" + "0"*n); (m * (m+1)) / 2 }


しかし最も短くシンプルなプログラムは、これだと思うw
(自分の中では半端な証明しか出来てないけど、たぶん合ってると思う)

def f3(n: Int) = ("5" + "0"*(n-1)) * 2

この件では、Hadoop君はScalaちゃんに敵わないようだ(笑)

コメント ( 0 ) | Trackback ( 0 )

Play framework2.0を触ってみた

前から「ScalaWebアプリを作るとしたらどのフレームワークがいいんだろう?」と思ってたんだけど。
名前を聞いたことあるものだけでもこれくらいある^^;

  • Play framework
  • Lift
  • Scalatra (RubyのSinatraライク
  • Unfiltered
  • Spray (Akkaベース
  • BlueEyes

(jugyoさん訳の紹介を見ると、SprayBlueEyesはWebサービス向けのようだから、ちょっと毛色が違うかも?)

本来だったら全部試して比べてみるのがいいんだろうけど、さすがにそこまで出来ないorz
でも試すとしたらPlayとUnfilteredになるのかな?
Liftはyuroyoroさんのサイトで紹介されているけど、当のyuroyoroさんはScalatraを推してる模様w
でもScalatraはRubyのフレームワークに似ているらしく、ScalaでわざわざRubyっぽいものをやりたいとは思わないので、後回しー。

さらに、自分の周りではPlayを採用していく雰囲気なので、Play framework 2.0を試してみた。


まず、インストールが簡単に出来るのがいいね。
雛形(サンプル)が作られるのでざっと追ってみたけど、分かり易いし。(ビューの呼び出し方は独特なはずだが、簡単に想像できる)

HTMLがそのまま書けて、可変部だけキーワードの様にしてScalaの式を埋め込めるのも便利。つまり、普通のHTMLエディターがそのまま使えるから。
(カスタムタグを使う方式だと、普通のHTMLエディターでは役に立たない。tableタグとかはHTMLエディターが無いとやってられんw)
HTMLに埋め込んだキーワード(Scalaの式)や設定ファイルに間違った記述があると、ブラウザーで画面を開くときにエラー箇所が表示されるので分かり易い。 

wiki(翻訳ドキュメント)も分かり易くて便利。 

なかなか良いんじゃない?(笑)

コメント ( 0 ) | Trackback ( 0 )

ScalaのREPL上でHadoopのSequenceFileを操作する

ScalaのREPL上でHadoopのHDFSを操作するツールで、SequenceFileの表示部分を強化した。

Path(ファイル)に対してshowを実行すると、SequenceFileかどうかを自動判別し、SequenceFileであればWritableを文字列化して表示する。
WritableにtoStringが実装されていないときはゲッターメソッドを呼び出してそれらの値を表示する。
AsakusaFWのWritableはtoStringが実装されているのでそのまま表示する)

自分で言うのもナンだけど、超便利!(笑)

また、100件ずつ表示するmoreコマンドを用意。
catは今まで全件表示にしていたけれど、headと同じに変更。件数が多いファイルを不用意に指定すると悲惨なことになるという経験をしたので(爆)
tailはスキップバイト数(行数ではない)を指定できるようにしたので、大きなファイルの末尾でも高速化した。1行当たりのバイト数というのは計算では出せないので、スキップバイト数はユーザーが指定する必要があるけれど…。

コメント ( 0 ) | Trackback ( 0 )

ScalaのREPL上でHadoopのHDFSを操作する

ScalaのREPL上でHadoopのHDFSを操作してみた(笑)
で、それを便利?に扱うライブラリーを作ってみた。

ちなみに、ソースはGitHub公開してみた。初Git!
まだEclipseからの直接の連携は出来てないけど、ソースを公開するには便利だなー^^


今回作ろうと思ったきっかけは、Pigを使っていて、ファイル名を変数で扱いたいなーと思ったこと。と、ファイルに簡単なデータを直接書き込めればいいのになーと思ったこと。
(Pigはlsとかcdとかgetとかを使ってファイル一覧を見たりするのは便利だけど、プログラミング言語ほどの高機能ではないので。
また、PigもHiveもHueも簡単なデータを入れるような機能は無い)

そこで、我等がScalaの出番(笑)
ScalaはREPLがあるので対話型で使えるし、Hadoopは結局Javaのクラスだから、呼び出せるはず!
という読みがバッチリ当たった。まぁ、意外なところで色々と(バグを踏んだりして)はまったけどね^^;

ただ単にファイルが見られるだけとかだったらPigの二番煎じで大して便利でもないかーと思ってたけど、作ってみたら意外と便利かもしれないと思った。
ちょっとしたテストデータを作ったり見たりするには良さそう。
今はテキストファイルだけしか想定してないけど、シーケンスファイルを読み込むのだって難しい話じゃないもんな。AsakusaFWで作ったファイルを直接見るとかも(クラスパスとインポートだけ追加すれば)出来るかも?


さて、本当は次のステップとしてREPL上で直接Mapper・Reducerを書いてHadoopで実行しちゃうという野望があったんだけど。
Hadoopを実行する際はクラスをjarファイル化する必要があるので、そこがクリアできてない…。

一応、:powerモードを使って、「intp.virtualDirectory」で仮想ファイル一覧が取れて、それを実ファイルシステム上に保存したらclassになるというところまでは発見したんだけど。
これって自分が定義したクラスそのものではない(REPL上での実行用のラッパークラスっぽい)ので、直接は使えなさそうなんだよねー。

なんか良い方法ないかなー。

コメント ( 0 ) | Trackback ( 1 )

Scala2.9で追加されたコレクションのメソッド

昨日新PCを買ったが、届くのは今度の金曜日なので、それまでプログラミングは控えるw

というわけで、前からやりたいと思っていた、Scala2.9で追加された“コレクションメソッド”を確かめてみた。
基本的に並列コレクション関連のメソッドが増えているだけ…かと思っていたら、地味に色々増えているようだ。確認して良かった(笑)
並列コレクションに関して「Gen」で始まるトレイトが増えているけど、これは直接使うわけでもないだろうから、まぁいいか。

(ついでにこの辺りのウェブページの構成を変えた。
具体的には、メソッド一覧の新しいページを作った。(コレクション関連の)トップページに置いておくには量がありすぎる^^;
(なので、他ページからの各メソッドへのリンクがずれてるかも(爆))
それと、並列コレクションのページはScala2.9の正式リリース前に試したものを書いていたので、修正した。メソッド名や方式がずいぶん変わってたし)

あと、REPLのScala2.9のコマンドも確認。
「:type」は地味に便利。「:sh」の使い方が変わったのはいいとして、「:implicits」がPredefのしか表示されないんだけど、これで正しいんだろうか…?

コメント ( 0 ) | Trackback ( 0 )

うかつなパッケージ名

AfwHSで、Javaから呼ばれる為のクラスを〜.afwhs.javaというパッケージに置いていた。
でも使う側で「import 〜.afwhs._」としたら、「java」から始まるパッケージのインポート「import java.io._」がエラーになってしまったらしい

Scalaではimportで相対パスのような指定が出来るけど、「_」でインポートしたら、どんなのが入るか使う人には分からないもんなー><

javaパッケージと一緒にインポートするのは試してなかったので気付かなかったけど、これはトラップだなぁ。「scala」パッケージと競合するのは経験してたから気をつけていたけれど、「java」もか…。
他にも色々あるようなので、気をつけないとなぁ(苦笑)

コメント ( 0 ) | Trackback ( 0 )

Scalaコンパイラプラグインによるソース変換

Scalaコンパイラプラグインの使い方がようやく分かってきた。

ソースの構文解析木を見るのはだいぶ前に試していた(試していたことすら忘れかけていた(爆))けれど、それは意外と簡単だった。
Scalaお得意のパターンマッチが炸裂・大活躍(笑)

難物だったのはソースコード変換
構文解析木のTreeだけ変えればいいのかと思ったら、Symbolも一緒に変えないといけない。しかもTreeの中にSymbolを保持していたり、いつの間にかContextでスコープを保持していたり…。分かってみれば対処法はあったけれど、分かるまでが大変だった(苦笑)
なにせサンプルが少なくて。検索してみると見つかるのが掲示板で、しかも質問は載っていても回答が付いていなかったり理解できなかったり(英語だし)。

しかし新しいTreeを作る為のTreeDSLは面白い。これぞDSLだな。Scalaの構文解析木を作る為のDSL。Scalaの面目躍如(笑)

何はともあれ、クラスフィールドメソッドを追加することは出来た。(やり方が確立してしまえば、意外と簡単)
これで何か自動生成できるかな〜?

コメント ( 0 ) | Trackback ( 0 )
« 前ページ