気になる開発プロダクツ

第7回Scala 2.6.0-final

Scalaは、オブジェクト指向と関数型というそれぞれの要素を兼ね備えた設計がされているスクリプト言語とその実装(処理系)です。スイス連邦工科大学ローザンヌ校(EPFL)のMartin Odersky教授によって、2001年から設計が開始されました。最初の実装がリリースされたのは2003年です。これから紹介するバージョン2.6.0-finalのリリースは、2007年9月11日でした。

Scalaで開発されているプロダクトとして、WebフレームワークのlifeテストツールのRehersalScalaCheck振る舞い駆動設計(Behaviour-Driven-Design)フレームワークのspecsなどが挙げられます。また、ビルドツールMavenのScalaプラグインも存在します。

Scalaの実装は学内のProgramming Methods Laboratory(LAMP)を中心に行われています。配布ライセンスはBSDライクのScala Licenseです。実行にはJDK1.4以降が必要となります。

Java VM上で動作するスクリプト言語としては、JRubyJythonなどいくつかありますが、Scalaもそれらと同じようにコマンドラインシェル上で入力したソースコードを、コンパイルの手順を踏まずに実行することができます。一方で、ソースコードをJavaのclassファイルにコンパイルしてから実行することもできます。

本稿では短いプログラムを実行するまでの手順と、基本的なプログラミングについて紹介していきます。言語仕様の詳細はドキュメントのページにある各種の英語版PDFファイルを参照してください。ドキュメントの一部はWikiサイトで閲覧することもできます。

基本的な使い方

ダウンロードとインストール

ダウンロードは、ScalaをインストールするマシンにJDK1.4以上がインストール済みであることを確認してから行います。

ダウンロード用のモジュールは、OSプラットフォームを問わないJavaインストーラや、ZIP、GZIP、RPMなどの形式があります。読者の環境に合ったものを選択してダウンロードしてください。容量はファイルの種類によって異なりますが、約12~17Mバイトあります。

インストールはこれらを展開すれば完了です。必要があれば、展開時に生成されたルートディレクトリ(scala-2.6.0-final、以下%SCALA_HOME%とする)の中にあるbinディレクトリのフルパスを環境変数PATHに追加しておきます。そうすると、Scala関連のコマンドをカレントディレクトリの場所を問わず実行できます。

筆者のWindows環境でZIPをダウンロードし展開したところ、約30Mバイトのフォルダやファイルが生成されました。

コマンドラインシェルでのプログラム実行

Scalaのコマンドラインシェルはscalaコマンドで実行を開始します。終了するには:quitを入力します。コマンドラインシェルの実行から終了までの流れを図1に示します。ここではいわゆるHello Worldプログラムを実行していますが、日本語の文字列もとくに問題なく扱えることがわかります。

図1 コマンドラインシェルの実行と終了
%SCALA_HOME%\bin> scala                          ← コマンドラインシェル開始
Welcome to Scala version 2.6.0-final.
Type in expressions to have them evaluated.
Type :help for more information.

scala> object ScalaProgramming {                 ← ソースコード入力開始
     |   def main( args: Array[ String ] ) {
     |     println( "Scalaによるプログラミング" )
     |   }
     | }
defined module ScalaProgramming                  ← ソースコード入力終了

scala> ScalaProgramming.main( null )             ← プログラム実行
Scalaによるプログラミング                        ← 実行結果(メッセージ出力)

scala> :quit                                     ← コマンドラインシェル終了

%SCALA_HOME%\bin>

classファイルへのコンパイル

ScalaのソースコードをJavaのclassファイルにコンパイルするには、scalacコマンドを実行します。図1で入力していたソースコードをScalaProgramming.scalaとして、それをコンパイル後に実行する例を図2に示します。

図2 ソースコードのコンパイルと実行
> scalac ScalaProgramming.scala  ← ソースコードをコンパイル
> scala ScalaProgramming         ← コンパイル済みのclassファイルを実行
Scalaによるプログラミング        ← 実行結果

Eclipseプラグインのインストール

ScalaにはEclipseプラグインも用意されていますが、こちらはJRE5以上とEclipse SDK 3.3の環境で利用できます。リモートサイトのURLはhttp://www.scala-lang.org/downloads/scala-plugin/ です。プラグインのインストール時にプラグイン開発環境(PDE)が要求されることがありますので、「Europa Discovery Site」から該当するものを探して一緒にインストールしておきましょう(図3)。これは画面右の「Select Required」ボタンをクリックして自動的に選択することもできます。

筆者の環境ではインストール時に警告画面が表示されましたが、「Install」をクリックしたらインストールは無事に完了しました。

図3 プラグインのインストール(Eclipse PDEも一緒に指定)
図3 プラグインのインストール(Eclipse PDEも一緒に指定)

Scalaプロジェクトの作成

プラグインのインストールが完了すると、Scalaプロジェクトが作成できるようになります(図4)。そこで、プロジェクトを作成してみましょう。図5「Scala Project」を選択したら、あとは一般のJavaアプリケーションと同じように操作すれば、Scalaプロジェクトが作成されます(図6)。

図4 Scala関連のメニューが追加されている
図4 Scala関連のメニューが追加されている
図5 Scalaプロジェクトの作成
図5 Scalaプロジェクトの作成
図6 作成した直後のプロジェクト
図6 作成した直後のプロジェクト

EclipseでのScalaアプリケーション開発

さっそくソースコードの入力、といきたいところですが、その前に必ずsrcフォルダのすぐ下にパッケージを作成しておかなくてはなりません。図4のメニューから「Scala Package」を選択してソースコード入力用のパッケージを作成しましょう。

パッケージの作成を終えたら、ここにアプリケーションとなるソースコードを入力するファイルを作成します。リスト1で実行したような、それ自体が起動できるようなものを作成するときは、図4から「Scala Application」を選択します。そしてダイアログにファイル名を入力した後に「Finish」クリックするとScalaパースペクティブが開き、ソースコードを入力できるようになります。

完成したプログラムを実行した例を図7に示します。Scalaパースペクティブといっても、見た目や操作は一般的なJavaパースペクティブとそれほど違いはありません。ソースコードもハイライトされて読みやすくなっており、筆者の環境では日本語の文字列も特別な設定変更をせずに扱うことができました。

図7 ソースコードの入力と実行(Scalaパースペクティブ)
図7 ソースコードの入力と実行(Scalaパースペクティブ)

Scalaプログラミングを少しだけ

では、Scalaのプログラミングを少しだけ試してみましょう。Java VM上で動作するとはいっても、Java言語とは異なる部分が多いため、はじめは戸惑ってしまうかもしれませんが、まずはトライしてみることで新しい世界が見えてくるでしょう。

objectとclass

図1を見て、Javaプログラマは不思議に思ったかもしれません。staticを宣言したわけでもないのに、ScalaProgrammingはnewでインスタンスを生成しないままに実行されてしまったからです。

では、ScalaProgrammingインスタンスをnewで生成できるのかといえば、それはできません( 図8)。objectで宣言された場合は、インスタンスを複製しないことを表しているためです。

図8 object宣言ではnewできない
scala> object ScalaProgramming {                  ← ソースコード入力開始
     |   def main( args: Array[ String ] ) {
     |     println( "Scalaによるプログラミング" )
     |   }
     | }
defined module ScalaProgramming                   ← ソースコード入力完了

scala> ScalaProgramming.main( null )              ← プログラム実行
Scalaによるプログラミング                        ← 実行結果

scala> var o = new ScalaProgramming()             ← インスタンスを変数oに代入?
<console>:4: error: not found: type ScalaProgramming   ← エラーが表示された!
var o = new ScalaProgramming()
            ^

scala>

classで宣言している場合は、newによりインスタンスを生成できます(図9)。objectとは異なり、インスタンスを生成しないと実行できません。両者の違いはJavaプログラマにはちょっと馴染まないところかもしれません。

図9 classならnewできる(Javaと同じ)
scala> class ScalaProgramming {                  ← classとして宣言してみる
     |   def main( args: Array[ String ] ) {
     |     println( "Scalaによるプログラミング" )
     |   }
     | }
defined class ScalaProgramming

scala> var o1 = new ScalaProgramming()           ← newでインスタンス生成
o1: ScalaProgramming = ScalaProgramming@168f248

scala> var o2 = new ScalaProgramming()           ← 別のインスタンスも生成
o2: ScalaProgramming = ScalaProgramming@805298

scala> println( o1 == o2 )
false                                            ← 2つは別のインスタンス

scala> o1.main( null )                           ← インスタンスでメソッド実行
Scalaによるプログラミング

scala> o2.main( null )
Scalaによるプログラミング

scala>

defはメソッド

defはメソッドを表しますが、メソッドの特殊形として代入文を記述することもできます(ただし代入文用にはvalやvarという予約語があります)。クラスを用いずメソッドだけを実行することが可能です。もちろん再帰処理も記述できます。図10に例を示します。

引数の型は引数の後に、戻り値の型はメソッドの後に、:に続けて記述しますが、戻り値の型は省略可能です。また、returnを記述しない場合は、最後に実行された文の結果がメソッドの戻り値となります。

図10 defによるメソッドや代入文の例
scala> def sum( n : Int ) = n * ( n + 1 ) / 2   ← 1からnまでの整数の総和を求めるメソッド
sum: (Int)Int         ← 戻り値の型は自動的にIntと判定された

scala> sum( 10 )
res0: Int = 55        ← 1から10までの整数の総和は55

scala> def a = 5      ← 代入文も記述できる(メソッドの特殊形)
a: Int                ← 値がInt型であることを自動的に判定

scala> def b = 1000L  ← 値の末尾にL(小文字でも良い)をつけてLong型に
b: Long

scala> def c = 3.2D   ← 値の末尾にD(小文字でも良い)をつけてDouble型に
c: Double

scala> def d = 5.5f   ← 値の末尾にf(大文字でも良い)をつけてFloat型に
d: Float

scala> def fact( n : BigInt ) : BigInt = if ( n == 0 ) 1 else ( n * fact( n - 1 ) )                  ← 再帰処理も記述可能
fact: (BigInt)BigInt

scala> fact( 100 )    ← 1から100までの階乗!
res1: BigInt = 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

scala> def resultFalse() = {  true;  false;  }  ← 戻り値がfalseになるメソッド
resultFalse: ()Boolean

scala> resultFalse()
res2: Boolean = false    ← 戻り値はfalse

scala> def resultTrue()  = {  false;  true;  }  ← 戻り値がtrueになるメソッド
resultTrue: ()Boolean

scala> resultTrue()
res3: Boolean = true     ← 戻り値はtrue

scala>

toとuntil

範囲を表すtoとuntilでは、最後の値が異なります。図11で示すとおり、1 to 5では1から5までの値を表すのに対し、1 until 5では、1から4までの値しか表しません。これらはRubyでいう1..5と1...5にあたります。

図11 toとuntilでは最後の値が異なる
scala> println( 1 to 5 )
Range(1, 2, 3, 4, 5)

scala> println( 1 until 5 )
Range(1, 2, 3, 4)

scala> println( 5 to 1 )
Range()

scala> println( 5 until 1 )
Range()

scala>

Rubyのブロックのような処理ができる

Scalaでは、Rubyでいうブロックのような処理を行うメソッドが用意されています。yieldという、ブロックを実行するメソッドもありますので、Rubyに近いプログラミングが可能です。図12がScalaのメソッド、図13がRubyのブロックの例です。両者でメソッド名や戻り値が異なる場合もありますが、ほぼ同じ処理が実行できることがわかります。

図12 Rubyのブロックのような処理
scala> (1 to 5).foreach( x => println( x ) )  ← 1から5までの値を表示
1
2
3
4
5

scala> (1 to 5).map( x => x * x )       ← 1から5までの値をそれぞれ2乗
res0: RandomAccessSeq.Projection[Int] = RangeM(1, 4, 9, 16, 25)

scala> (1 to 5).filter( x => x > 3 )    ← 3より大きな要素のみを抽出
res1: Seq.Projection[Int] = RangeF(4, 5)

scala> for( x <- 1 to 5 ) yield x * x   ← yieldも使える
res2: RandomAccessSeq.Projection[Int] = RangeM(1, 4, 9, 16, 25)

scala> def sqr( n : Int ) = for( x <- 1 to n ) yield x * x
dup: (Int)RandomAccessSeq.Projection[Int]

scala> sqr( 5 ).foreach( s => println( s ) )
1
4
9
16
25
図13 Rubyのブロック処理(参考) -irbで実行
> irb
irb(main):001:0> (1..5).each { |x| puts x }
1
2
3
4
5
=> 1..5
irb(main):002:0> (1..5).map { |x| x * x }
=> [1, 4, 9, 16, 25]
irb(main):003:0> (1..5).find_all { |x| x > 3 }
=> [4, 5]
irb(main):004:0> def sqr( n )  for x in 1..n; yield x * x; end end
=> nil
irb(main):005:0> sqr( 5 ) { |s| puts s }
1
4
9
16
25
=> 1..5
irb(main):006:0>

リストや配列ではないタプル

IPアドレスやデータベースのテーブルの1行分のデータのように、複数の要素が集まって1つの集合になっている値というものが存在します。それがタプル(Tuple)です。いったん定義された要素は更新できないというのがリストや配列とは異なる特徴です。PythonXQueryでもタプルの定義が可能になっています。

タプルの要素は左から_1, _2, ...のように指定します。図14に例を示します。

図14 タプルの要素の指定
scala> var p = (3.0, 4.0)
p: (Double, Double) = (3.0,4.0)

scala> var x = p._1
x: Double = 3.0

scala> var y = p._2
y: Double = 4.0

scala> Math.sqrt( x * x + y * y )
res0: Double = 5.0

scala>

"""~"""とXML

複数行にわたる長い文字列は"""~"""で記述できます。これはPythonと同じです。そしてXML自体をソースコードに記述できます。これはXQueryと同じです。ソースコード内にこうしたものが記述されているのはJavaプログラマにとっては驚きかもしれませんが、PythonやXQueryの世界ではあたりまえのことなのです。その両方を体験できるScalaは貴重な存在ではないかと思います。

図15に示すとおり、XMLデータが代入された場合は、\や\\を用いて必要なノードのみを抽出することもできます。これはXPathの/や//を真似たものと推測できます。

図15 """~"""とXMLの代入
scala> var html = """<html>
     | <head><title>Scalaでプログラミング</title></head>
     | <body><h1>その1 基本的なこと</h1></body>
     | </html>"""
html: java.lang.String =
<html>
<head><title>Scalaでプログラミング</title></head>
<body><h1>その1 基本的なこと</h1></body>
</html>

scala> var xml = <html>
     | <head><title>Scalaでプログラミング</title></head>
     | <body><h1>その1 基本的なこと</h1></body>
     | </html>
xml: scala.xml.Elem =
<html>
<head><title>Scalaでプログラミング</title></head>
<body><h1>その1 基本的なこと</h1></body>
</html>

scala> xml \ "head"
res0: scala.xml.NodeSeq = <head><title>Scalaでプログラミング</title></head>

scala> xml \ "body"
res1: scala.xml.NodeSeq = <body><h1>その1 基本的なこと</h1></body>

scala> xml \\ "title"
res2: scala.xml.NodeSeq = <title>Scalaでプログラミング</title>

scala> xml \\ "h1"
res3: scala.xml.NodeSeq = <h1>その1 基本的なこと</h1>

scala>

.や()をつけなくてもよい

Javaプログラミングでは、メソッド名の前には.(ピリオド)がつき、引数を囲むのには()が必要です。しかしScalaでは、図16のように、空白で区切られたものの意味や優先順位が明白であれば、これらを記述しなくても動作する場合があります。

図16 .や()がなくても動作する
scala> Math.sqrt( 5.0 )  ← Javaでは一般的な記述
res18: Double = 2.23606797749979

scala> Math sqrt 5.0     ← .や()を記述しなくても動作する
res19: Double = 2.23606797749979

scala> 1 to 5 foreach x => println x   ← 優先順位が不明確でうまく動作しない
<console>:1: error: not a legal formal parameter
1 to 5 foreach x => println x
       ^

scala> (1 to 5).foreach( x => println( x ) )  ← ()で優先順位を示せば動作する
1
2
3
4
5

scala>

今回紹介できたのはScalaプログラミングのほんの入り口に過ぎません。その先にはJavaでは体験できない世界が遠くまで広がっています。サンプルソースなどを参照しながら、新しい体験をしてみてください。これまで見えていなかったものが見えてくるかもしれません。

おすすめ記事

記事・ニュース一覧