変数
Scalaの変数にはValue (val
)とVaraible (var
)二種類あります。val
は一度定義したら、値を変えることができないので、関数型プログラミングによく使われます。
val or var?
これ結構自分の中でも質問になっていました。Function Programming(FP)は不変 ということを重視していて、つまりサイドエフェクトを避けるため、変数の値を変えるのが良くないと考えられます 。なので、FPではval
を使うのが普通です 。「じゃあ、var
いらなくない?」という疑問があります。Scalaは純粋のFP言語ではなく、OOPもサポートしているので、var
が存在するのはもう一つの選択肢を提供することで、使うかどうかは人によります。なお、var
をキャッシュなどに利用することで、FPプログラムのパフォーマンス向上に役立ちます。
文字列(String)
Scalaの文字列オブジェクトはすごい使いやすい。その辺はスクリプト言語とかわらないぐらい便利:
val hello = "Hello World"
hello . drop ( 6 ). take ( 2 ). toUpperCase // WO
hello . endsWith ( "World" ) // true
hello . drop ( 2 ). take ( 2 ). equals ( "ll" ) // true
Scalaの文字列は配列型なので、JavaScriptのsplit
のようなメソードはScalaにもある:
val animal = "dog, cat, pig, cow, duck"
animal . split ( ',' ). map ( _ . trim ) // Array ( dog , cat , pig , cow , duck )
mapは配列の要素を指定したメソードに渡すメーソドです。_.trim
はscalaの無名関数で、_
は渡された引数を格納されている。つまり、map(_.trim)
は配列にあるすべての要素の前後のスペースを消すという挙動になる。(詳しくは後ほど)
ScalaのStringはCharの配列として扱われる。例えば、”Hello”(1) //e
、文字列の直後にindexを書けば、そのindexにある文字が取得できる。
指定したフォーマットで文字列をプリントするには、Scalaのf
というメソードを使う:
println ( f "You got $money%.2f yen!" ) // You got 1000.00 yen!
println ( f "You got $money%.0f yen!" ) // You got 1000 yen !
s
を使うと、$<変数名>
で変数と文字列を混ぜて出力できる:
println ( s "My name is $name" )
Function
Functionの定義はdef
を使います:
def incrOne ( a : Int ) : Int = a + 1
無名関数:
val list = List ( 1 , 2 , 3 , 4 , 5 )
list . foreach ( ( a : Int ) => println ( a * 5 ))
List
はscalaのCollectionタイプで、foreach
、map
など便利なイテレーションメソードが使えます。
上記の例では(a: Int) => println(a * 5)
が無名関数です。=>
の左側が入力で、右側が出力となります。この例でa: Int
というタイプを指定していますが、Scalaは自動的にタイプを認識してくれるので、a => println(a * 5)
と書いても大丈夫です。
Scalaの無名関数はもっとシンプルな書き方ができます:
val list = List ( 1 , 2 , 3 , 4 , 5 )
list . map ( _ * 5 ). foreach ( println )
この例では_ * 5
が無名関数です。_
は入力される変数になります。これを穴埋め問題と考えるとわかりやすいかもしれません。list
中の全ての要素をこの穴に埋めて、結果を返すって感じです。
初めてこの文法を見てわけわからなかったが、慣れたらすごいわかりやすい記述だなと思いました。これはオブジェクトのリストにも活用できます:
class Player ( var name : String , var score : Int = 0 ) {
override def toString = s "$name : $score"
// これがscalaのクラスです、詳しくは後で説明します。
// パッと見ると関数の定義と似ていますが、nameとscoreはPlayerクラスのメンバー変数になります。
// toStringをoverrideで定義すると、インスタンスをprintlnに渡すと、このメソードをコールされます。
val players = List [ Player ](
new Player ( "Asuka" , 9000 ),
new Player ( "Rei" , 999999 ),
new Player ( "Shinji" , 200 )
val ranking = players . sortWith ( _ . score > _ . score )
_.score > _.score
はソート用の無名関数で、最初の_
は関数の第一引数で、次の_
が第二引数になります。この関数をちゃんと書くと(a: Int, b: Int) => a.score > b.score
になります。Scalaは無名関数の引数の数を見て引数をスマートに_
に入れてくるので、簡単な処理はこの短いフォーマットがおすすめです。
List, Array, Set, Map
scalaのコレクションタイプの中では、 よく使うのはList
, Array
, Set
とMap
です。
List
List
は1種類のデータを順番に繋がるデータ構造である。なので、List
は順番のイテレーションが得意だが、ランダムアクセスが苦手 (特に最後の要素にアクセスには全ての要素を経由しないといけない)
val list1 = List ( 1 , 2 , 3 , 4 , 5 , 6 )
// List(1, 2, 3, 4, 5, 6)
val list2 = List . range ( 1 , 10 , 2 )
val list3 = 2 :: 4 :: 6 :: 8 :: 10 :: Nil
List
に要素を追加する
val list4 = list2 :: 100 :: 200
// List(100, 200, 1, 3, 5, 7, 9)
val list5 = Seq ( 100 , 200 ) ++ list2
// List(1, 3, 5, 7, 9, 100, 200)
val list6 = list2 :+ 100 :+ 200
// List(1, 3, 5, 7, 9, 100, 200)
val list7 = list1 ++ Seq ( 7 , 8 , 9 , 10 );
// List ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 )
Seq
はList
, Set
の親クラスである
List
によく使うメソード
val list8 = list1 . map ( _ * 5 )
// List(5, 10, 15, 20, 25, 30)
list1 . foreach ( a => println ( a * a ))
val sorted = list1 . sortWith ( _ > _ )
// List(6, 5, 4, 3, 2, 1)
Array
Array
は簡単に言えばindex
付きの配列です。
val array = Array ( "Asuka" , "Rei" , "Shinji" )
// array自身はイミュータブルなんですが(サイズは変えられない)、中の要素はミュータブルです。
array . foreach ( i => println ( s "Index of $i is " + array . indexOf ( i )))
もしサイズ可変の配列を使いたければ、Scala.collection.ArrayBuffer
を使ってください:
val arraybuf = scala . collection . mutable . ArrayBuffer ( "Asuka" , "Rei" )
arraybuf ++= Seq ( "Illustrious" , "Ayanamirei" )
arraybuf . foreach ( i => println ( s "Index of $i is " + arraybuf . indexOf ( i )))
// Index of Illustrious is 4
// Index of Ayanamirei is 5
Array
はイミュータブルので、要素の削除するには、要素を削除した結果を他のval
に保存するか、最初にvar
を宣言して、結果を自身に再アサインするかのいずれかになります。
val array1 = Array ( "Asuka" , "Shinji" )
array1 ( 1 ) = null // remove Shinji
val array2 = array1 . filter ( _ != null )
var array3 = Array ( "Rei" , "Shinji" )
Set
Set
はユニークな要素を格納するコンテナである。
val set1 = Set ( 1 , 2 , 2 , 3 , 4 , 4 , 5 , 6 , 3 , 6 , 7 , 8 )
// Set ( 5 , 1 , 6 , 2 , 7 , 3 , 8 , 4 )
イミュータブルバージョンのSet
よりも個人的には可変のscala.collection.mutable.Set
のほうが出番が多いと思います。
var set2 = scala . collection . mutable . Set ( "Rei" , "Asuka" )
set2 ++= Seq ( "Shinji" , "Misato" )
ちなみに、Set
に重複してる要素を追加しても、Exceptionが投げられません。 その特徴を利用して、ユニークな要素を確保したい場合はscala.collection.mutable.Set
を使えば良いと思います。
Map
ScalaのMap
はkey -> value
という構造のコレクションです。
val map = Map ( "name" -> "Asuka" , "power" => 9999 )
println ( "The power of" + map ( "name" ) + " is " + map ( "power" ))
もちろん、可変バージョンのscala.collection.mutable.Map
もあります:
class Player ( var score : Int = 0 , val name : String )
class Eva ( val name : String , var power : Int = 0 )
val evas = scala . collection . mutable . Map (
new Player ( name = "Rei" , score = 100 ) -> new Eva ( "零号機" , 9000 ),
new Player ( name = "Asuka" , score = 200 ) -> new Eva ( "弐号機" , 9999 )
evas ( new Player ( name = "Shinji" , score = 900 )) = new Eva ( "初号機" , 7000 )
println ( s "${x._1.name} is using ${x._2.name}" )
Script言語を使ってきた私は、key
がオブジェクトにできるのが少し新鮮で、結構便利ではないかと思いました。
引き続きはhttp://befool.co.jp/blog/chainzhang/basic-of-scala-2/ へ