はじめに
おそらくネット上にあるもっとも優れたScala教材の一つであろうScala Schoolについて、
気になったことや試したことなどを部分的に忘備録としてメモしておこうと思います。
Scala Schoolは説明が非常にさっぱりしているのでその補足になればいいかなと思います。
Lessonsごとに記事は分けます。まずは最初のlessonであるbasicsから。
本編
Functions
まずは関数定義の基本の形。
def addOne(m: Int): Int = m + 1
戻り値の型(ふたつめの: Int
の部分)は省略して書けるらしい。(リスト3など)
Anonymous functions
val a = (x: Int) => x + 1
a(5) //結果は6
val b = (x: Int, y: Int) => x + y //複数の引数ももちろんOK
b(5,6) //結果は11
val c = (x: Int)(y: Int) => x + y //NG. 匿名関数で引数を2つのかっこに分ける形はできない
val d = (x: Int) => (y: Int) => x + y //どうしても匿名でcurriedの形で書きたい場合はこうする
d(3)(7) //結果は10
scalaではパターンcのような引数を複数のかっこに分けて書く形をcurried functionとしています(後述)が、本来的にはdの書き方のほうがクロージャを利用した正統なcurried functionなのかもしれません。
匿名でないcurried functionについては後述。
Partial applicaiton
def adder(m: Int, n: Int) = m + n //まず定義(戻り値の型を省略しています)
val addTwoA = adder(2, _:Int) //これで部分適用できる
val addTwoB = adder(2, _) //型指定を省略してもOK
val addTwoC = adder(_, 2) //最後の引数だけでなくどの引数でもワイルドカードにできる
addTwoA(3) //結果は5
Curried functions
def multiply(m:Int)(n:Int): Int = m * n //関数を定義
multiply(2)(3) //もちろん同時に両方のパラメーターを入力してもOK. 結果は6
val timesTwoA = multiply(2) _ //OK
val timesTwoB = multiply(2)(_) //OK
val timesTwoC = multiply _(2) //これはエラー
val timewTwoD = multiply(_)(2) //これもエラー
val timesTwoE = multiply(_:Int)(2) //型指定を入れればOK
timesTwoA(3) //結果は6
パターンDはmissing parameter typeというエラーになりますが理由がよくわかりません。
stackoverflowで聞いてみてはいますが、まだ回答なし。(わかる方いたらお願いします)
Variable length arguments
def capitalizeAll(args: String*) = {
args.map { arg =>
arg.capitalize
}
}
個人的に少しひっかかったこのコード。実は引数は{}でも入力することができるようです。
実際
println{"asdf"}
これでも動きます。
式そのものも、オブジェクト・値としてあつかうのでこのようになっているのかもしれませんね。javaの習性であれ?と思いました。
Classes
基本的なクラス定義の書き方。
class Calculator {
val brand: String = "HP"
def add(m: Int, n: Int): Int = m + n
}
val calc = new Calculator //Javaと同様にnewでインスタンスを作る
calc.add(1, 2) //メソッドの呼び出しも同じ感じ
calc.brand //フィールドへのアクセス. 文字列HPが返ってくる
Constructor
クラス内のインスタンスメソッド定義の外側に書いたコードがコンストラクタとみなされるようです。
Abstract Classes
Javaと同じような感じで抽象クラスもあります。extends
するのも同じ感じ。
abstract class Shape {
def getArea(): Int
}
class Circle(r: Int) extends Shape {
def getArea(): Int = {r * r * 3}
}
Traits
フィールドと振る舞いをまとめたインターフェース的なもの。
トレイトは複数extendsできるので、抽象クラスよりこっちを使うほうが基本的にはよいとのこと。
trait Car {
val brand: String
}
trait Shiny {
val shineRefraction: Int
}
class BMW extends Car with Shiny {
val brand = "BMW"
val shineRefraction = 12
}
scala schoolは残念ながらscala3には対応していないようですが、
scala3では以下のようにwith
を使わずシンプルにカンマつなぎで複数extendsができる!
class BMW extends Car, Shiny {
val brand = "BMW"
val shineRefraction = 12
}
Types
型パラメーターは[]
で囲むらしい。