LoginSignup
20
12

More than 5 years have passed since last update.

Scala入門 基礎編「Scalaの書き方を理解しよう」

Last updated at Posted at 2017-12-22

前提

Scalaの勉強で覚えたことを備忘録的にズラズラ書いていきます。
細かい説明は省略します。

Scala入門

プロジェクト作成

  1. ディレクトリを作成
  2. Scalaのバージョン情報を書き込んだ設定ファイルを用意
  3. 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)
    }
  }
}
20
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
12