まあ、playはじめましたと変わらないんだけど。
チュートリアルはとりあえず「playだけ」を学びたかったんで、Javaでやりました。経験上、これもこれも必要だから同時にやっちゃおう!というのは往々にして失敗するので。「英語も大事なので、○○の勉強は英語のテキスト使おう」みたいな。結局コストかかってやんなくなるという……。
まあ器用な人はできると思うけど……。僕はできそうにないので一個一個不器用なりに着実にやっていきましょう。
次こそ本命のScalaに。
今回は勉強にコレ使いました。
Scalaの構文とか色々
しばらく特筆すべきことがない……。普通の言語と同じなとこはガンガン飛ばして行って、ここをメモ代わりに使っていこう。
糖衣構文の箇所が面白かったです。配列の要素指定に()を使う話です。あの()って、applyやらupdateやらと同じらしく。
scala> val num = a2(1) //配列の要素を取得
num: Int = 2
scala> a2 //配列内容を表示
res25: Array[Int] = Array(100, 2, 3)
scala> a2(1) //値の取得
res31: Int = 2
scala> a2.apply(1) //値の取得
res32: Int = 2
scala> a2(1) = 10 //値の設定
scala> a2.update(1,10) //値の設定
a(0)はArrayに定義されてるapplyメソッドの呼び出し。
a(0)=10はArrayに定義されているupdateメソッドの呼び出し。
この()の要素指定がapply/update呼び出しの糖衣構文(シンタックスシュガー)といい、読み書きのしやすさのために導入される構文。
シンタックスシュガー!!かっこいい!
響きが気に入りました!しばらくなんとかの一つ覚えのように使う。
Scalaの演算子
Scalaでは演算子は全てメソッド。
scala> 1 + 2
res8: Int = 3
scala> 1 .+(2) //1の後に「.」を付けると、Doubleと判断されるので、スペースを入れる
res9: Int = 3
Scalaはメソッドが一つの引数を取る場合、ピリオドと()を省略できる。だからメソッド呼び出しが演算子のように見える。
おもしろい!
演算子はJavaと同じ。ただインクリメントとデクリメントは無い。+=と-=を使う。
コレクション
Scalaの代表的なコレクションを勉強していく。JavaだとArrayList, HashMapさえありゃーどうにかなったけど、Scalaではどうだろう。
リスト型
Listは先頭項目の追加/削除が高速だが、ランダムアクセス(添字を使用した要素へのアクセス)が遅い。先頭から順番に任意に処理していくのに向いている。
ただ、Listを使ってしまうとList型は不変コレクションだから一度定義すると変更できない。変更がある場合はListBufferを使用する。
あと、List型は::メソッドを持っている。要素同士の結合を意味する。
scala> val list1 = Nil
list1: scala.collection.immutable.Nil.type = List()
scala> val list2 = "Scala" :: list1
list2: List[java.lang.String] = List(Scala)
scala> val list3 = "Java" :: list2
list3: List[java.lang.String] = List(Java, Scala)
headメソッドとtailメソッドでListの先頭と、先頭を除く要素のListを返す。
マップ型
連想配列、HashMap。こんな感じで宣言する。
Map[キーの型,値の型](キー1->値1, キー2->値2, ...)
実際のコードではこんな感じ。
scala> val map = Map[String,Int]("Scala"->1,"Java"->2,"Ruby"->3)
map: scala.collection.immutable.Map[String,Int] = Map(Scala -> 1, Java -> 2, Ruby -> 3)
値取り出しは()でキーを指定する。 シンタックスシュガーですね。
scala> m("Scala")
res50: Int = 1
mapで使用するメソッド表。
メソッド | 説明 |
---|---|
empty | 空のmapを返す |
+ | 指定したキーと要素のペアを追加したmapを返す |
- | 指定したキーの要素を抜いたmapを返す |
size | mapのサイズを返す |
KeySet | キーのセットを返す |
values | mapの値をiterableで返す |
おお、iterable!やっぱiteratorあるよね。よかった。
タプル型
異なる要素を格納可能なList?かな。値をカンマで区切り()で囲んで定義する。ただ、要素の取得が特殊で、_1みたいに(アンダースコアと取得したい要素のindex)指定すればアクセスできる。こんな感じ。
scala> val t1 = ("Scala",10)
t1: (java.lang.String, Int) = (Scala,10)
scala> val t2:Tuple2[String,Int] = ("Java",20)
t2: (String, Int) = (Java,20)
scala> val t = ("Scala",10,100.0d)
t: (java.lang.String, Int, Double) = (Scala,10,100.0)
scala> t._1
res71: java.lang.String = Scala
scala> t._2
res72: Int = 10
for文
for文は普通とちょっと違う。Scalaにインクリメントは無いので、ちょっとiterator的な書き方になる。
for ([ジェネレータ] [フィルタ]*無くてもいい) 式
ジェネレータ部分はitem<-listItems的な感じで、iterator的というか、for each的に書く。
ListItemから要素を取り出し、printで出力するソース
scala> val listItems = List("Scala","Java","Ruby")
listItems: List[java.lang.String] = List(Scala, Java, Ruby)
scala> for(item <- listItems) println("item = " + item)
item = Scala
item = Java
item = Ruby
for文でうんたんし結果を保持したい場合もある。そのときはyieldを使用する。
scala> val resultList = for(item <- listItems) yield "I use " + item
resultList: List[java.lang.String] = List(I use Scala, I use Java, I use Ruby)
scala> resultList
res33: List[java.lang.String] = List(I use Scala, I use Java, I use Ruby)
Scalaのfor文はいろいろ多機能で、yieldの他にも色々あるっぽい。
例外処理
try {
式
} catch {
case 変数:例外クラスの型 => 例外処理の式
......
} finally {
式
}
実際のコードだとこんな感じ?
scala> val result = try {
| "a".toInt
| } catch {
| case e:NumberFormatException => {
| println("exception!")
| -1
| } finally {
| println("finally!")
| }
exception!
finally!
result: Int = -1
例外処理はthrowを使用する。
scala> throw new Exception("throw Exception!")
java.lang.Exception: throw Exception!
at .<init>(<console>:8)
……
match
Javaでいうswitch。break書かなくてもおk。default処理は_ => 式。以上。
あー、あと条件式を型で記述できる。case i:Int みたいな。
関数
おおお。関数型言語の関数。本題か。
関数型言語とは関数をファーストクラスオブジェクト(関数を引数や戻り値にできる)として扱えることを利用し、副作用を持たない関数を使用してプログラムを構築する手法。
「副作用を持たない」ってのはその関数の持つ変数とかに意図しない変更が起きないように、らしい。
なんで関数をファーストクラスオブジェクトとして扱えることが副作用がないとできるんだろう。という疑問を持って勉強する。
関数はdefで宣言する。
def 関数名[型パラメータ](引数名:引数の型名,...):返り値=関数本体
int型の引数を2つ取り、Int型を返す「add」という関数を定義してみる。
scala> def add(x:Int, y:Int):Int = x + y
add: (x: Int, y: Int)Int
こんな感じで、値を返すときにreturnはいらない(別につけてもいい)。
あ、あとvoidを使いたいならunitとすればよい。
次に、関数を引数としてとる関数を定義する。
scala> def calc(f:(Int,Int)=>Int, num:Int) :Int =f(num,num)
calc: (f: (Int, Int) => Int, num: Int)Int
scala> calc((x,y) => x + y,10)
res13: Int = 20
もちろん事前に関数を定義してそれを渡すこともできる。
scala> val add = (x:Int, y:Int) => x + y
add: (Int, Int) => Int = <function2>
scala> calc(add, 10)
res14: Int = 20
高階関数
返り値に関数オブジェクトを返す関数を定義する。getFuncという関数を定義する。ちなみに仕様は以下。
引数:String
戻り値:「引数:Int,Int 戻り値:String」な関数
scala> def getFunc(str:String): (Int,Int)=>String = (x:Int, y:Int) => str + (x + y)
getFunc: (str: String)(Int, Int) => String
scala> val f = getFunc("result is ")
f: (Int, Int) => String = <function2>
scala> f(1,2)res17: String = result is 3
うう、ここらへん慣れないなー。=>とか=とか。まあ、腕動かして、腕に覚えさせるしかないね。
クロージャ
その関数のスコープにおける引数意外の変数を参照できる関数。getterで取るなどして直接値をいじるんじゃなくて関数内で閉じる。カプセル化として重要。以下の感じ。
scala> def counter() = {
| var count = 0
| () => {
| count += 1
| count
| }
| }
counter: ()() => Int
scala> val c1 = counter()
c1: () => Int = <function0>
scala> c1()
res50: Int = 1
scala> c1()
res52: Int = 2
counter関数は「インクリメントする関数」を返す。だから() =>見たいな妙な書き方をしている。関数型では最後に評価された式を返すから、counter()で最後に評価された式は() => {...}となる。これは()メソッドを返す。=>{の中を見てみるとインクリメント...って寸法。
クラス生成
ホントはコンストラクタとかthisとかいろいろごちゃごちゃ書かれてたんだけど、「言語を一つ持ってて、その言語を使ってコーディングするプログラマクラス」の例を一個あげてそれで終わる。
scala> class Programmer(val language: String) {
| /** コンストラクタ **/
| println("Programmerインスタンスを生成します")
| println("language = " + language)
|
| /**引数がnullだったらエラー。「引数なし」はいいけど、nullはだめ。**/
| require(_language != null)
| /** 補助コンストラクタ */
| def this() = this("Scala")
|
| def coding() = println(language + "を使ってコーディングします")
| }
defined class Programmer
scala> val programmer1 = new Programmer
Programmerインスタンスを生成します
language = Scala
programmer1: Programmer = Programmer@1852a3ff
scala> val programmer2 = new Programmer("Java")
Programmerインスタンスを生成します
language = Java
programmer2: Programmer = Programmer@628c4ac0
scala> programmer
programmer1 programmer2
scala> programmer1.coding
Scalaを使ってコーディングします
scala> programmer2.coding
Javaを使ってコーディングします
このサンプルありゃあだいたいわかるよね。あと、thisはJavaと同じような使い方もできるよ。
アクセス修飾子
Scalaのアクセス修飾子は注意。アクセス修飾子をつけなかったらなんと publicになる。Javaみたいに「修飾子なし」という修飾子にはならない。privateはあるよ。
オーバーロード
あるよ!
シングルトン
Scalaのシングルトンはすごい簡単。Javaだとprivateとstaticでゴニョゴニョするんだけど、Scalaならclass修飾子の代わりにObject修飾子をつけるだけ。わかりやすい!そりゃあnewできないよね。classじゃなくてobjectだし。そりゃあ一個しか実態もたないよね。classじゃなくてobjectだし。
と、とりあえずその1はここらへんで終わっとくか。キリがなさげ。また気が向いたらScalaの勉強再開します。しばらくはplayに夢中になっとくかーーーー。