前提
Scalaの勉強で覚えたことを備忘録的にズラズラ書いていきます。
細かい説明は省略します。
Scala入門
プロジェクト作成
- ディレクトリを作成
- Scalaのバージョン情報を書き込んだ設定ファイルを用意
- sbt起動
$ mkdir scala_project
$ cd scala_project
$ echo 'scalaVersion := "2.12.4"' > build.sbt
$ sbt
...
sbt:scala_project>
変数 - val と var の使い分け
object MyApp {
def main(args: Array[String]): Unit = {
// 変数
// - val: 値の再代入ができない immutable
// - var: 値の再代入ができる mutable
// val msg: String = "Hello world"
// val msg = "Hello world" // 型推論(データ型が自明の場合)
var msg = "Hello world"
msg = "Hello world again?"
println(msg)
}
}
データ型
object MyApp {
def main(args: Array[String]): Unit = {
// 整数 Byte Short Int Long
val i = 5 // この場合は型推論で Int となる
val l = 5555555555L
// 浮動小数点数 Float Double
val d = 32535.5
val f = 234.34F
// 文字 char
val c = 'a'
// 文字列 String
val s = "Hello"
// 論理値 Boolean
val flag = true // or false
// 特殊文字 \n: 改行、 \t: タブ
val msg = "Hello w\norl\td"
println(msg)
}
}
演算
object MyApp {
def main(args: Array[String]): Unit = {
// 数値
// + - * / %
val x = 10
println(x / 3) // 3
println(x / 3.0) // 3.3333...
println(x % 3) // 1
var y = 5
// y = y + 5
y += 5 // 10
// 文字列
val s = "Hello "
println(s + "world")
// 論理値
// and or not
// && || i
val flag = true
println(!flag) // false
}
}
文字列
object MyApp {
def main(args: Array[String]): Unit = {
val name = "taguchi"
val score = 80
val height = 158.3
println(s"name: $name, score: $score, height: $height")
println(s"name: $name, score: ${score + 10}, height: $height")
println(f"name: $name%10s, score: $score%10d, height: $height%10f")
println(f"name: $name%10s, score: $score%-10d, height: $height%10f")
println(f"name: $name%10s, score: $score%-10d, height: $height%10.2f")
}
}
if
object MyApp {
def main(args: Array[String]): Unit = {
val score = 70
// > >= < <= == != && || !
// if (score > 80) println("Great!")
// else if (score > 60) println("Good!")
// else println("so so ...")
// Scalaではif文は値を返す
val result =
if (score > 80) "Great!"
else if (score > 60) "Good!"
else "so so ..."
println(result)
}
}
match
object MyApp {
def main(args: Array[String]): Unit = {
// match
val signal = "pink"
val result = signal match {
case "red" => "stop"
case "blue" | "green" => "go"
case "yellow" => "caution"
// case _ => "wrong signal" // 他の言語で言うdefault
case other => s"wrong signal: ${other}" // 検索対象の値をそのまま変数で受け取る事もできる
}
println(result)
}
}
while
object MyApp {
def main(args: Array[String]): Unit = {
// while
// 0 - 9
var i = 100
// while (i < 10) { // 最初に判定を行う
// println(i)
// i += 1 // Scalaでは++の記法はないので注意
// }
do {
println(i)
i += 1
} while (i < 10) // 後判定
}
}
for
object MyApp {
def main(args: Array[String]): Unit = {
// for (ジェネレータ) 処理
// for (変数 <- データの集合) 処理
// 0 to 3, 0 until 4
// for (i <- 0 to 3) println(i)
// for (i <- 0 to 3; j <- 0 to 3) println(s"$i, $j")
// for (i <- 0 to 3; j <- 0 to 3 if i != j) println(s"$i, $j")
val result = for (i <- 0 to 3; j <- 0 to 3 if i != j) yield s"$i, $j" // yield = イールド
for (element <- result) println(element)
}
}
メソッド
object MyApp {
// method
// def sayHi: Unit = { // 意味のある値を返さない場合の返り値の型
def sayHi: String = {
// println("hi!")
// return "hi!"
"hi!" // Scalaでは最後に評価された値が単に返されるので、returnを省略することもできる
}
def main(args: Array[String]): Unit = {
// sayHi
println(sayHi)
}
}
引数の使い方
object MyApp {
// method
def sayHi(name: String = "taguchi", age: Int = 23): Unit = {
println(s"hi! $name ($age)")
}
def main(args: Array[String]): Unit = {
sayHi("bob", 35)
sayHi("tom", 55)
sayHi()
sayHi(age = 18, name = "steve")
}
}
クラス
// Class
// user: User
// i: Int
// - 変数 / フィールド
// - メソッド
class User {
val name = "my name"
def sayHi() = println("hi!") // メソッドの返り値の型は省略(型推論)、かつメソッドの中身も一行なので {} 省略
}
object MyApp {
def main(args: Array[String]): Unit = {
// val user: User = new User
val user = new User // 型推論がきくので型宣言を省略しても良い
println(user.name)
user.sayHi()
}
}
コンストラクタ引数を使う
// class User(_name: String) {
class User(val name: String) { // コンストラクタ引数をそのままフィールドにする場合にvalやvarを付けてあげる
// val name = _name
// def sayHi() = println("hi" + this.name)
def sayHi() = println("hi! " + name) // どこのnameか自明の場合はthis省略可
}
object MyApp {
def main(args: Array[String]): Unit = {
// val tom: User = new User("tom")
val tom = new User("tom") // インスタンス
println(tom.name)
tom.sayHi()
}
}
継承
// User -> AdminUser へ継承
// 親クラス -> 子クラス という
// スーパークラス -> サブクラス ともいう
class User(val name: String) {
def sayHi() = println("hi! " + name)
}
class AdminUser(name: String, val age: Int) extends User(name) {
def sayHello() = println("hello! " + name + "(" + age + ")")
override def sayHi() = println("[admin] hi! " + name) // 親メソッドの上書き
}
object MyApp {
def main(args: Array[String]): Unit = {
val bob = new AdminUser("bob", 23)
println(bob.name)
println(bob.age)
bob.sayHi()
bob.sayHello()
}
}
パッケージでクラスを管理
パッケージファイル
package com.scala_lessons.model
class User(val name: String) {
def sayHi() = println("hi! " + name)
}
class AdminUser(name: String, val age: Int) extends User(name) {
def sayHello() = println("hello! " + name + "(" + age + ")")
override def sayHi() = println("[admin] hi! " + name) // 親メソッドの上書き
}
パッケージを利用するファイル
import com.scala_lessons.model.User
import com.scala_lessons.model.AdminUser
// import com.lesson17.model.{User, AdminUser} // 複数呼び出す場合
// import com.lesson17.model._ // Package内のすべてのクラスを呼び出す場合
object MyApp {
def main(args: Array[String]): Unit = {
// val tom = new com.scala_lessons.model.User("tom")
val tom = new User("tom")
val bob = new AdminUser("bob", 23)
}
}
アクセス修飾子
// アクセス修飾子
// (public) <- 何もつけないとデフォルトで公開となる
// private <- そのクラスからのみアクセスできる
// protected <- そのクラスのサブクラスからのみアクセスできる
class User {
// val name = "user"
// protected val name = "user"
private val name = "user"
def sayHi() = println("hi! " + name)
}
class AdminUser extends User {
override def sayHi() = println("[admin] hi! " + name) // nameがprivateなのでエラーになる
}
object MyApp {
def main(args: Array[String]): Unit = {
val user = new User
val adminUser = new AdminUser
// println(user.name)
user.sayHi()
adminUser.sayHi()
}
}
final修飾子
// final
// - class: 継承できない
// - members: overrideできない
class User {
// final class User {
// final val name = "user"
val name = "user"
def sayHi() = println("hi! " + name)
}
class AdminUser extends User {
override val name = "user"
override def sayHi() = println("[admin] hi! " + name)
}
object MyApp {
def main(args: Array[String]): Unit = {
val user = new User
val adminUser = new AdminUser
println(adminUser.name)
}
}
オブジェクト
// object
object User { // 同名の場合、コンパニオンオブジェクト
def getInfo() = println("User Object")
def apply(name: String) = new User(name) // ファクトリーメソッド
}
class User (val name: String) { // 同名の場合、コンパニオンクラス
def sayHi() = println("hi! " + name)
}
object MyApp {
def main(args: Array[String]): Unit = {
User.getInfo()
// val tom = new User("tom")
// val tom = User.apply("tom")
val tom = User("tom") // 省略して書ける
Predef.println(tom.name) // printlnはPredefオブジェクトのメンバー
}
}
抽象クラスを作ってみる
// 抽象クラス
// User: 抽象クラス
// - Japanese: 具象クラス
// - American: 具象クラス
abstract class User {
def sayHi()
}
class Japanese extends User {
def sayHi() = println("こんにちは!")
}
class American extends User {
def sayHi() = println("hi!")
}
object MyApp {
def main(args: Array[String]): Unit = {
val aki = new Japanese
val tom = new American
aki.sayHi()
tom.sayHi()
}
}
トレイトで機能を合成する
// trait
trait Printable {
def print() = println("now printing ...")
}
trait Sharable {
def share() = println("now sharing ...")
}
class User extends Printable with Sharable
object MyApp {
def main(args: Array[String]): Unit = {
val user = new User
user.print()
user.share()
}
}
トレイト多重継承時のメンバー衝突を解決する
// trait
// 多重継承時の同名メンバー衝突回避策
// - その1:継承するクラス側でオーバーライド(してどちらのメンバーを使用するか選択)する
// trait Printable {
// def print() = println("now printing ...")
// def getInfo() = println("print!")
// }
// trait Sharable {
// def share() = println("now sharing ...")
// def getInfo() = println("share!")
// }
// class User extends Printable with Sharable {
// override def getInfo() = super[Printable].getInfo()
// }
// - その2:共通トレイトを作成しメンバーに優先順位をつける(後から合成したトレイトのメンバーにてオーバーライドされる)
trait common {
def getInfo()
}
trait Printable extends common {
def print() = println("now printing ...")
override def getInfo() = println("print!")
}
trait Sharable extends common {
def share() = println("now sharing ...")
override def getInfo() = println("share!")
}
class User extends Printable with Sharable
object MyApp {
def main(args: Array[String]): Unit = {
val user = new User
user.getInfo() // Sharableの方が後からwithしているので「share!」と表示される
}
}
型をパラメータ化する
// 型パラメータ
// class MyInt {
// def getThree(i: Int): Unit = println(s"$i $i $i")
// }
class MyData[T] {
def getThree(i: T): Unit = println(s"$i $i $i")
}
object MyApp {
def main(args: Array[String]): Unit = {
// val mi = new MyInt
// mi.getThree(3)
val i = new MyData[Int]
i.getThree(5)
val s = new MyData[String]
s.getThree("hello")
}
}
関数オブジェクト
// 関数 Input -> Output
object MyApp {
// method
def multi(a: Int, b: Int) = a * b
// 関数オブジェクト
// - 引数 => 処理
// val multiFunc: (Int, Int) => Int = (a: Int, b: Int) => a * b
// val multiFunc = (a: Int, b: Int) => a * b
val multiFunc = (_: Int) * (_: Int) // 処理の中で引数を一度しか使わない場合はプレースホルダーが使える
def main(args: Array[String]): Unit = {
// println(multiFunc(3, 5)) // 15
println(multiFunc(2, 4)) // 8
}
}
関数のカリー化
object MyApp {
def main(args: Array[String]): Unit = {
// 関数のカリー化
val multiFunc = (a: Int, b: Int) => a * b
val multiFuncCurried = (a: Int) => ((b: Int) => a * b)
// println(multiFunc(3, 5))
// println(multiFuncCurried(3)(5))
val double = multiFuncCurried(2) // double = (b: Int) => 2 * b
val tripple = multiFuncCurried(3) // tripple = (b: Int) => 3 * b
println(double(5)) // 2 * 5 = 10
println(tripple(5)) // 3 * 5 = 15
}
}
部分適用
object MyApp {
// 部分適用
def msg (from: String, to: String, text: String) = s"($from -> $to): $text"
def main(args: Array[String]): Unit = {
// println(msg("taguchi", "fkoji", "OK!"))
// println(msg("sugita", "fkoji", "Great!"))
// println(msg("tanaka", "fkoji", "Good!"))
val msgToFkoji = msg(_: String, "fkoji", _: String) // 部分的に引数をプレースホルダーにする
val msgFunc = msg _ // すべての引数をプレースホルダーにする
println(msgToFkoji("taguchi", "OK!"))
println(msgToFkoji("sugita", "Great!"))
println(msgToFkoji("tanaka", "Good!"))
}
}
Tuble:タプル
object MyApp {
def swap(a: Int, b: Int) = (b, a)
def main(args: Array[String]): Unit = {
// tuple
// タプルはなんでも入れられるが、23個以上は入れられない制限あり
// val data = (12, "taguchi", 52.4) // tupleは括弧で囲う
// println(data._1)
// println(data._2)
// println(data._3)
val(x, y) = swap(30, 20) // 返り値がタプル(複数の値)の場合は、それを受ける変数もタプル型にする
println(x) // 20
println(y) // 30
}
}
List:リスト
object MyApp {
def main(args: Array[String]): Unit = {
// Collection(データの集合)
// - Seq: List (シークエンス: 順番を持つデータ集合)
// - Set (セット:順番を持たない&重複しないデータ集合)
// - Map (マップ:キーと値でデータを管理していく集合)
// val scores = List(200, 300, 400)
// val scores = List[Int](200, 300, 400) // 型は省略可
// val scores = Nil // 空のリストを作成
// val scores = 200 :: Nil // からのリストの先頭に値を追加
val scores = 200 :: 300 :: 400 :: Nil
println(scores.length) // 3
println(scores.isEmpty) // false
println(scores.head) // 200
println(scores.tail) // List(300, 400)
println(scores(1)) // 300
}
}
Set:セット
object MyApp {
def main(args: Array[String]): Unit = {
// Collection(データの集合)
// - Seq: List (シークエンス: 順番を持つデータ集合)
// - Set (セット:順番を持たない&重複しないデータ集合)
// - Map (マップ:キーと値でデータを管理していく集合)
val answers = Set(5, 3, 8, 5)
println(answers) // 5, 3, 8
println(answers.contains(3)) // true
println(answers(3)) // true .contains()の省略形
val set1 = Set(1, 3, 5, 8)
val set2 = Set(3, 5, 8, 9)
println(set1 & set2) // 積集合:& or intersect ※両方に入っている要素を集めたもの
println(set1 | set2) // 和集合:| or union ※すべての要素が最低限一つ含まれていること
println(set1 &~ set2) // 差集合:$~ or diff ※ある集合の中から別の集合に属する要素を取り去って得られる集合のこと
}
}
Map:マップ
object MyApp {
def main(args: Array[String]): Unit = {
// Collection(データの集合)
// - Seq: List (シークエンス: 順番を持つデータ集合)
// - Set (セット:順番を持たない&重複しないデータ集合)
// - Map (マップ:キーと値でデータを管理していく集合)
// - Key -> value
val sales = Map("taguchi" -> 200, "fkoji" -> 400)
println(sales("taguchi")) // 200
// println(sales("not match")) // error
println(sales.contains("not match")) // false
println(sales.getOrElse("not match", -1)) // -1
}
}
Mutableなコレクションを使う
import scala.collection.mutable.{ Map => MutableMap }
object MyApp {
def main(args: Array[String]): Unit = {
// Collection(データの集合)
// - Seq: List (シークエンス: 順番を持つデータ集合)
// - Set (セット:順番を持たない&重複しないデータ集合)
// - Map (マップ:キーと値でデータを管理していく集合)
// Immutable 変更不可(関数型プログラミングで推奨されている)
val scores = Map("taguchi" -> 40, "fkoji" -> 80)
val scores2 = scores.updated("taguchi", 60) // 値を変更した別のMapを返してくれる
// Mutable 変更可
val scores3 = MutableMap("taguchi" -> 40, "fkoji" -> 80)
}
}
map, filter, foreachを使う
object MyApp {
def main(args: Array[String]): Unit = {
val prices = List(53.2, 48.2, 32.8)
// prices.map((n: Double) => n * 1.08)
// prices.map(n => n * 1.08)
prices.map(_ * 1.08).filter(_ > 50).foreach(println)
}
}
case classを使ったパターンマッチング
// case class: Immutable
// -> クラスの構造やフィールドの内容でパターンマッチさせることができる
case class Point(x: Int, y: Int) // コンストラクタ引数にはval, varは不要
object MyApp {
def main(args: Array[String]): Unit = {
val points = List(
Point(5, 3),
Point(0, 0),
Point(3, 4)
)
points.foreach(_ match {
case Point(0, 0) => println("origin!")
case Point(5, _) => println("right edgh!") // yは使わないためプレースホルダーを付けておく
case Point(x, y) => println(s"$x:$y")
})
}
}
option型で値を返す
object MyApp {
def main(args: Array[String]): Unit = {
// Error
// Option 何らかのエラーが起きた際にnullや例外を投げる代わりに値を返す
// - some 値が存在した時
// - None 値が存在しない時
val scores = Map("taguchi" -> 50, "fkoji" -> 80)
// println(scores("not match")) // getOrElse, contains
scores.get("not match") match { // Mapのgetメソッドの返り値がOption型になっている
case Some(v) => println(v)
case None => println("key not found")
}
}
}
Either型でエラー処理
object MyApp {
// Error
// Either エラーの内容を返す
// - Right
// - Left
def div(a: Int, b: Int): Either[String, Int] = {
if (b == 0) Left("Zero div error!")
else Right(a / b)
}
def main(args: Array[String]): Unit = {
// div(10, 0) match {
div(10, 3) match {
case Right(n) => println(n)
case Left(s) => println(s)
}
}
}