Scala は JVM 上で動作するバイトコードにコンパイルできる言語です。JAVA よりも柔軟な記述ができます。事前にこちらからダウンロードおよびインストールしておいてください。基本的な文法をまとめます。
変数および定数
object HelloWorld {
def main(args: Array[String]): Unit = {
val constVal = 1 // 定数
var variableVal = 1 // 変数
val intVal: Int = 1 // 型指定定数
var intVar: Int = 1 // 型指定変数
// 基本的な型
val longVal = 1L
val floatVal = 1.0f
val doubleVal = 1.0
val charVal = 'a'
val stringVal = "string"
val nullVal = null
// Any 型 (Scala の全ての親クラス)
val a: Any = 1
a.asInstanceOf[Int] // キャスト
}
}
条件分岐
object HelloWorld {
def main(args: Array[String]): Unit = {
if (true) {
println("true")
}
else {
println("false")
}
val myval = if (true) 1 else 0
}
}
ループ処理
object HelloWorld {
def main(args: Array[String]): Unit = {
var i = 0
while(i < 10) {
i = i + 1
}
i = 0
do {
i = i + 1
} while (i < 10)
}
}
ブロックおよび関数
ブロック
object HelloWorld {
def main(args: Array[String]): Unit = {
val result = {
val a = 1
val b = 2
a + b
}
println(result) //=> 3
}
}
関数
object HelloWorld {
def main(args: Array[String]): Unit = {
def myFunc1(a: Int, b: Int): Int = {
return a + b
}
def myFunc2(a: Int, b: Int): Int = {
a + b
}
def myFunc3(a: Int, b: Int) = {
a + b
}
def myFunc4(a: Int, b: Int) = a + b
def myFunc5 = 3
println(myFunc1(1,2)) //=> 3
println(myFunc2(1,2)) //=> 3
println(myFunc3(1,2)) //=> 3
println(myFunc4(1,2)) //=> 3
println(myFunc5) //=> 3
}
}
クラス定義
引数なしコンストラクタ
object HelloWorld {
def main(args: Array[String]): Unit = {
class MyClass {
val prop = 1
def method(arg: Int) = prop + arg
}
val obj = new MyClass
println(obj.method(2)) //=> 3
}
}
引数ありコンストラクタ
object HelloWorld {
def main(args: Array[String]): Unit = {
class MyClass(_arg: Int) {
val prop = _arg
def method(arg: Int) = prop + arg
}
val obj = new MyClass(1)
println(obj.method(2)) //=> 3
}
}
コンストラクタに記載した処理はインスタンスを生成したタイミングで実行されます。
object HelloWorld {
def main(args: Array[String]): Unit = {
class MyClass {
println(123)
}
val obj = new MyClass //=> 123
}
}
継承
object HelloWorld {
def main(args: Array[String]): Unit = {
// 親クラス
class MyClass {
val prop1 = 123
var prop2 = "abc"
def method: Unit = println("MyClass")
}
// インスタンス化
val obj = new MyClass
println(obj.prop1) //=> 123
obj.prop2 = "xyz" // 'var' にはセッターがある
println(obj.prop2) //=> xyz
// 子クラス
class MySubClass extends MyClass {
override val prop1 = 456 // val には 'override' が必要
prop2 = "xyz" // var には 'override' は不要 (というよりは「上書きする」)
override def method: Unit = { //メソッドのオーバーライド
super.method // 親クラスは super で参照
println("MySubClass")
}
final val prop3: String = "final でオーバーライドできなくする。"
}
val obj2 = new MySubClass
obj2.method //=> MyClass\nMySubClass
println(obj2.prop1) //=> 456
println(obj2.prop2) //=> xyz
println(obj2.prop3) //=> final でオーバーライドできなくする。
}
}
抽象クラス
object HelloWorld {
def main(args: Array[String]): Unit = {
abstract class MyAbstractClass {
val prop: String
def method(arg: Int): Int
}
class MyConcreteClass extends MyAbstractClass {
val prop: String = "abc"
def method(arg: Int): Int = {
arg + 1
}
}
val obj = new MyConcreteClass
println(obj.prop) //=> abc
println(obj.method(1)) //=> 2
}
}
インスタンスは何のクラスであるか
クラスオブジェクトを比較して検証できます。isInstanceOf を利用したほうがスマートです。
object HelloWorld {
def main(args: Array[String]): Unit = {
val i = 123
println(classOf[Int]) //=> int
println(i.getClass == classOf[Int]) //=> true
}
}
無名クラス
import scala.language.reflectiveCalls
object HelloWorld {
def main(args: Array[String]): Unit = {
val obj = new {
val prop = 123
}
println(obj.prop) //=> 123
}
}
シングルトン
Singletonパターンは Scala では Object で実現できます。
object HelloWorld {
def main(args: Array[String]): Unit = {
object Singleton {
val prop = 123
}
println(Singleton.prop) //=> 123
}
}
シノニム typedef (C++) のようなもの
object HelloWorld {
def main(args: Array[String]): Unit = {
type MyType[A] = List[List[A]]
val obj: MyType[Int] = List(List(1), List(2))
println(obj) //=> List(List(1), List(2))
type Three[A] = Tuple3[A, A, A]
type M[A, B] = scala.collection.mutable.Map[A, B]
}
}
ジェネリッククラス
C++ クラステンプレート のように型を既定しないクラスを実装できます。
object HelloWorld {
def main(args: Array[String]): Unit = {
class MyClass[A] {
var list: List[A] = Nil
}
val obj = new MyClass[Int]
obj.list = 1 :: obj.list
obj.list = 2 :: obj.list
println(obj.list) //=> List(2, 1)
}
}
配列
object HelloWorld {
def main(args: Array[String]): Unit = {
val intArr = new Array[Int](10)
for(elem <- intArr) {
println(elem)
}
}
}
配列バッファー
可変長の配列です。リストと異なり配列ですのでインデックスで指定した要素へのアクセスは高速ですが、要素の追加や削除の処理は現在のサイズ分のメモリ領域を新しく確保しなおす必要があるため低速です。
object HelloWorld {
def main(args: Array[String]): Unit = {
import scala.collection.mutable.ArrayBuffer
val buf = new ArrayBuffer[Int]()
buf += 1
buf += 2
3 +=: buf
println(buf) //=> ArrayBuffer(3, 1, 2)
}
}
リスト
object HelloWorld {
def main(args: Array[String]): Unit = {
// これは配列
val intArr = new Array[Int](2)
intArr(0) = 10
intArr(1) = 20
println(intArr(0)) //=> 10
println(intArr(1)) //=> 20
// 以下はリスト
// Nil と空リストは等しい
println(List() == Nil) //=> true
// リストを結合で生成
val list = 1 :: 2 :: List(3,4) ::: List(5,6)
println(list) //=> List(1, 2, 3, 4, 5, 6)
println(list(0)) //=> 1
println(1 :: 2 :: Nil) //=> List(1, 2) 空のリスト Nil の先頭に要素を追加
val newList1 = list :+ 1 // 末尾に追加
val newList2 = 1 +: list // 先頭に追加
println(newList1 ++ newList2) // 連結 (リストの場合は ::: でもよい)
println(list ++ intArr) // 左辺の型 List になる
println(list ++: intArr) // 右辺の型 Array になる
// カウント
println(list.count(s => s < 6)) //=> 5
println(list.count{ _ < 6 }) //=> 5
// 存在確認
println(list.exists{ _ == 1 }) //=> true
println(list.contains(0)) //=> false
// すべてが〜である、かどうか
println(list.forall{ _ > 0 }) //=> true
// 先頭の要素、最後の要素
println(list.head) //=> 1 (空のリストでは例外)
println(list.last) //=> 6 (空のリストでは例外)
println(Nil.headOption) //=> None
println(Nil.lastOption) //=> None
// 先頭を除いたリスト、最後を除いたリスト
println(list.tail) //=> List(2, 3, 4, 5, 6)
println(list.init) //=> List(1, 2, 3, 4, 5)
// 先頭から N 個取得または破棄
println(List(1,2,3,4,5).take(2)) // 先頭から二つ List(1, 2)
println(List(1,2,3,4,5).drop(2)) // 先頭から二つを除いたもの List(3, 4, 5)
println(List(1,2,3,4,5).takeWhile{ _ < 4 }) //=> List(1, 2, 3)
println(List(1,2,3,4,5).dropWhile{ _ < 4 }) //=> List(4, 5)
// 一定範囲のみを取得
println(List("a", "b", "c", "d", "e").slice(2, 4)) // slice(i,j) = List(Ei, Ei+1,..., Ej-1)
//=> List(c, d)
// 空であるかどうか (一つだけ見ればよいため size == 0 より高速)
println(list.isEmpty) //=> false
println(list.nonEmpty) //=> true
// リストの長さ
println(list.length) //=> 6
println(list.size) //=> 6
// マップ
println(list.map{ _ * 2 }) //=> List(2, 4, 6, 8, 10, 12)
// 文字列の生成 (Join のようなもの)
println(list.mkString(", ")) //=> 1, 2, 3, 4, 5, 6
// 逆転
println(list.reverse) //=> List(6, 5, 4, 3, 2, 1)
// 要素の削除 (フィルタリングして除外して新たなリストを作成)
println(list.filter{ _ == 1 }) //=> List(1)
println(list.filterNot{ _ > 2 }) //=> List(1, 2)
// ソート
println(list.sortWith((s,t) => s < t)) // sort
//=> List(1, 2, 3, 4, 5, 6)
// map でチェーンする場合に高速化
// (中間 List を生成せずに map も適用しながら filter するため withFilter が高速)
val result = list.filter { _ != 1 } map { _.toString }
val result2 = list.withFilter { _ != 1 } map { _.toString }
println(result) //=> List(2, 3, 4, 5, 6)
println(result2) //=> List(2, 3, 4, 5, 6)
}
}
リストバッファー
リストはその性質上、末尾へのアクセスが遅くなります。末尾への要素の追加が頻繁に発生して速度が気になる場合はリストバッファーを使用します。
object HelloWorld {
def main(args: Array[String]): Unit = {
import scala.collection.mutable.ListBuffer
val buf = new ListBuffer[Int]
buf += 10
buf += 20
30 +=: buf
println(buf) //=> ListBuffer(30, 10, 20)
val list = buf.toList
println(list) //=> List(30, 10, 20)
}
}
タプル
object HelloWorld {
def main(args: Array[String]): Unit = {
// タプルには異なる型が格納できる
val pair: Tuple2[Int, String] = (99, "This is a string.")
println(pair._1 + 1) //=> 100
println(pair._2 + 1) //=> This is a string.1
// 例えば、リストではできない (Any になる)
val list: List[Any] = List(99, "This is a string.")
println(list.head.asInstanceOf[Int] + 1) // Any からキャストする必要がある
println(list.last.asInstanceOf[String] + 1) // Any からキャストする必要がある
// 型を明記する例
val tuple:(Int,Int) = (1, 2)
// 値の取得
val (intValA, intValB) = tuple
println(intValA) //=> 1
println(intValB) //=> 2
println(tuple._1) //=> 1
println(tuple._2) //=> 2
tuple match {
case(intValC, intValD) => {
println(intValC) //=> 1
println(intValD) //=> 2
}
}
}
}
集合と連想配列
object HelloWorld {
def main(args: Array[String]): Unit = {
// 集合
import scala.collection.mutable.Set
val mySet = Set("A", "B")
mySet += "C"
println(mySet.contains("C")) //=> true
// 連想配列
import scala.collection.mutable.Map
val myMap = Map[String, Int]()
myMap += ("A" -> 1)
myMap += ("B" -> 2)
println(myMap("B")) //=> 2
val myMap2 = Map(
"a" -> 10,
"b" -> 20
)
println(myMap2) //=> Map(b -> 20, a -> 10)
}
}
Java で null 判定していた箇所には Option 型を活用
object HelloWorld {
def main(args: Array[String]): Unit = {
// 連想配列
val myMap = Map(
"a" -> 10,
"b" -> 20
)
// キーが存在する場合
println(myMap("a")) //=> 10
println(myMap.get("a")) //=> Some(10)
println(myMap get "a") //=> Some(10)
// キーが存在しない場合
// println(myMap("c")) //=> java.util.NoSuchElementException
println(myMap.get("c")) //=> None
// Option 型はパターンマッチで条件分岐
// ( Scala の Option[T] は null と比較しても意味がないため
// Java のように if-else で null 判定してはならない )
def show(x: Option[Int]) = x match {
case Some(i) => i
case None => null
}
println(show(myMap get "a")) //=> 10
println(show(myMap get "c")) //=> null
// Option 型は Some で囲えば作れる
println(Some(1)) //=> Some(1)
println(None) //=> None
}
}
Option 型の便利なメソッド
object HelloWorld {
def main(args: Array[String]): Unit = {
val option: Option[Int] = Some(123)
val option2: Option[Int] = null
val option3: Option[Int] = None
// Map の get と区別。Option 型の get
println(option.get)
// println(option2.get) // java.lang.NullPointerException
// println(option3.get) // java.util.NoSuchElementException: None.get
// 以下 null はすべて例外を投げるため除外:
// 定義済みかどうかを判定
println(option.isEmpty) //=> false
println(option.isDefined) //=> true
println(option3.isEmpty) //=> true
println(option3.isDefined) //=> false
// None の場合に null または指定した値を返す
println(option.orNull) //=> 123
println(option.getOrElse(-1)) //=> 123
println(option3.orNull) //=> null
println(option3.getOrElse(-1)) //=> -1
// None の場合は処理しない
option.foreach{ value: Int =>
println(value) //=> 123
}
option3.foreach{ value: Int =>
println(value) // None のため実行されない
}
}
}
ファイルの読み込み
import scala.io.Source
object HelloWorld {
def main(args: Array[String]): Unit = {
if(args.length > 0) {
for(line <- Source.fromFile(args(0)).getLines())
println(line.length + " " + line)
}
else
Console.err.println("Please enter filename")
}
}
正規表現
object HelloWorld {
def main(args: Array[String]): Unit = {
println("string".matches("""\w{6}""")) //=> true
val pattern = """(\d\d\d\d)-(\d\d)-(\d\d)""".r
val res = "9999-12-31" match {
case pattern(year, month, day) => year + "/" + month + "/" + day
case _ => false
}
println(res) //=> 9999/12/31
}
}
コンソール出力
object HelloWorld {
def main(args: Array[String]) : Unit = {
print("print\n") // 改行なし
println("println") // 改行あり
printf("%d / %d = %f\n", 1, 2, 1/2.0) // 書式指定
println("%d / %d = %f".format(1, 2, 1/2.0)) // 書式指定
}
}
コンソール入力
import scala.io.StdIn._
object HelloWorld {
def main(args: Array[String]) : Unit = {
// 書式指定, 有限個 readfN (N=1,2,3)
val (year: String, month: String, day: String) = readf3("{0}/{1}/{2}") // タプルを返す
println("year: %s".format(year))
println("month: %s".format(month))
println("day: %s".format(day))
// 書式指定, 無限個 readfN (N=1,2,3)
val list: List[Any] = readf("{0}/{1}/{2}")
println("year: %s".format(list(0)))
println("month: %s".format(list(1)))
println("day: %s".format(list(2)))
// 一行そのまま
val line = readLine("line: ")
println(line)
// 型を指定して検証
print("int: ")
val myInt = readInt // readDouble, readLong, readBoolean 等もあります
println(myInt)
assert(myInt > 0, "%d は 0 以下です。".format(myInt))
}
}
遅延評価
lazy
を指定すると、最初に参照されたタイミングで式が評価されるようになります。アプリケーションの起動速度の改善などにつながります。
object HelloWorld {
def main(args: Array[String]) : Unit = {
var i = 0;
lazy val a = i * 2; // ここでは式が評価されない
val b = i * 2;
i = 1;
println(a) //=> 2 ←ここで式 i*2 が評価される
println(b) //=> 0
i = 2;
println(a) //=> 2 // 再評価はされない
}
}
外部コマンドの実行
import scala.sys.process._
import scala.language.postfixOps
object HelloWorld {
def main(args: Array[String]): Unit = {
val cmd = "ls"
val code: Int = cmd ! // 終了ステータス ('!' の後には空行が必要)
println(code) //=> 0
val res: String = cmd !! // 結果文字列 ('!!' の後にも空行が必要)
println(res)
}
}
アクセサをクラスに追加
JavaBeans 形式のアクセサ (ゲッターとセッター) をクラスに追加できます。
import scala.beans.BeanProperty
object HelloWorld {
def main(args: Array[String]): Unit = {
case class MyClass(
@BeanProperty var prop1: Int,
@BeanProperty var prop2: String)
val obj = new MyClass(123, "abc")
obj.setProp2("xyz")
println(obj.getProp1) //=> 123
println(obj.getProp2) //=> xyz
}
}
Java の資産を利用
Scala からは Java のパッケージがそのまま利用できます。例えば Java の Random や Date を利用するには以下のようにします。
import java.util.Random
import java.util.Date
// import java.util._ // としてもよいです
// import java.util.* // ではないことに注意
object HelloWorld {
def main(args: Array[String]): Unit = {
val generator = new Random()
println(generator.nextInt())
val now = new Date()
println(now)
}
}
便利なリンク集
関連記事
- Scala 文字列の処理書式指定 object Main { def main(args: Array[String]): Unit = { println("%d + %d = %d".format(1, 1, 2)) //=> 1 + 1 = 2 } } 文字列の比較 ヒアドキュメント 他の言語でいう「ヒアドキュメント」のようなものは """ で囲うことで実現できます。 object Main ...
- Scala 日付に関する処理Date クラスを文字列にフォーマット import java.util.Date object Main { def main(args: Array[String]): Unit = { // format は Date に限らない文字列用の機能です。 println("%d-%d-%d" format (1, 1, 1)) //=> 1-1-1 printl...
- 酢豚の基本的な使い方 (sbt)sbt は Scala および Java を主な対象としたビルドツールです。Scala Build Tool の略ではありませんが、Simple Build Tool という明示的な記述も公式ドキュメントなどには見当りません。以下 sbt の基本的な使用例をまとめます。使用した sbt のバージョンは 0.13 です。 公式ドキュメント [sbt 0.13](http://www.scala-sb...
- Scala 関数のサンプルコード「デフォルト引数」および「Unit 型を返す関数」 object HelloWorld { def main(args: Array[String]): Unit = { def myPrint(myArg: String = "default_value") = println(myArg + "!") val result = myPrint() //=> defau...
- Scala 組み込みの制御構造if-else 条件分岐で知られる if-else は三項演算子のようにも使用されます。 object HelloWorld { def main(args: Array[String]): Unit = { val myVal = if (!args.isEmpty) args(0) else "default" println(myVal) } ...