Skip to content

Instantly share code, notes, and snippets.

@j5ik2o
Last active August 29, 2015 13:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save j5ik2o/8821516 to your computer and use it in GitHub Desktop.
Save j5ik2o/8821516 to your computer and use it in GitHub Desktop.

はじめてのScala

どんな言語?

  • オブジェクト指向パラダイムを主軸に関数型パラダイムを取り込んだプログラミング言語です。
  • コンパイラ言語であり、コンパイル結果はJavaバイトコードとなり、JVM上で実行可能です。

インストール方法

$ brew install scala

ビルドツール

$ brew install sbt

IDE

https://www.jetbrains.com/idea/download/

IntelliJ IDEA CE版をダウンロードしてください。

Hello, World!

  • REPL(Read Eval Print Loop)に直接コードを入力して実行する。
$ scala
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_40).
Type in expressions to have them evaluated.
Type :help for more information.

scala> println("Hello, World!")
Hello, World!
  • スクリプトモードで実行する。
$ echo 'println("Hello, World!")' > Script.scala
$ scala Script.scala
Hello, World!
  • コンパイルして実行する。
object Main extends App {
  println("Hello, World!")
}
$ scalac Main.scala
$ scala Main
Hello, World!

Scalaでプログラミング

変数と定数

  • 最代入可能な変数はvarを使って宣言する。変数の型名は変数名の後ろに記述する(型アノテーション)。
scala> var name: String = "java"
name: String = java

scala> name = "scala"
name: String = scala
  • 再代入不可能な定数をvalを使って宣言する。
scala> val name: String = "scala"
name: String = scala

scala> name = "java"
<console>:8: error: reassignment to val
       name = "java"

変数に型がある

文字列型には整数型を代入できません。

scala> var name: String = "scala"
name: String = scala

scala> name = 1
Int(1) <: String?
false
<console>:8: error: type mismatch;
 found   : Int(1)
 required: String
       name = 1

型推論

変数の型は値から型推論可能。

scala> val name = "scala"
name: String = scala

抽象型を明示的に指定したい場合は、型アノテーションを意図的に使う。

scala> val number = 1
number: Int = 1

scala> val number: Number = 1
number: Number = 1

クラスを実装する

お金(通貨の単位と、お金の量を保持する)を表現するクラスを実装する。

  • クラスの実装
package money

import java.util.Currency

class Money(val amount: Int, val currency: Currency)

val JPY = Currency.getInstance("JPY")
val m = new Money(1, JPY) // 1円

println(m == new Money(1, JPY)) // false
  • ケースクラスを利用する

newやvalを省略できるケースクラスの機能を利用する。

package money

import java.util.Currency

// caseを付けてvalを除く
case class Money(amount: Int, currency: Currency)

val JPY = Currency.getInstance("JPY")
val m = Money(1, JPY) // Money.apply(1, JPY)

println(m == Money(1, JPY)) // true : equalsメソッドが自動的に実装される
println(m) // Money(1, JPY) : toStringが自動的に実装される
  • クラスにメソッドを追加する
case class Money(amount: Int, currency: Currency) {

  def plus(other: Money): Money = {
    require(currency == other.currency)
    Money(amount + other.amount, currency)
  }
  
  def minus(ohter: Money): Money = {
    require(currency == other.currency)
    Money(amount - other.amount, currency)
  }
  
  def +(other: Money) = plus(other)
  
  def -(other: Money) = minus(other)

}


val m1 = Money(100, JPY).plus(Money(10, JPY))
println(m1) // Money(110, JPY)

val m2 = Money(100, JPY) plus Money(10, JPY)
println(m2) // Money(110, JPY)

val m3 = Money(100, JPY) + Money(10, JPY)
println(m3) // Money(110, JPY)

関数

関数はオブジェクトの一種です。変数に格納したり、メソッドの引数などに渡すことが可能です。

  • 引数なしで整数値を返す、関数を記述する
val f = { () => 1 }

println(f()) // println(f.apply()) // 1
  • 引数を二倍にして返す、関数を記述する
val f = { x: Int => x * 2 }

println(f(2)) // f.apply(2) // 4
  • メソッドの引数や戻り値に関数を利用する
def foreach(array: Array[Int], f: Int => Unit) = {
  for (value <- array) f(value)
}

foreach(Array(1, 2, 3), { n => println(n) })

よく使う型

  • Option型(Maybeモナド)

Scalaではnullは使いません(Javaプログラムと連携する時のみ使う)。つまり、NullPointerExceptionがない世界です。

case class Person(id: Int, name: String)

def findById(id: Int): Option[Person] = 
  if (id % 2 == 0) Some(Person(id, "a")) else None

val pOpt1 = findById(1) // Some(Person(1, "a"))
val pOpt2 = findById(2) // None

val p1 = pOpt1.get // Person(1, "a")
val p2 = pOpt2.get // NoSuchElementException
val p3 = pOpt2.getOrElse(Person(1, "b")) // Noneの時に返すデフォルト値を指定
  • タプル型

複数個の値を一塊に扱う型

val t2 = (1, 2)
println(t2._1 + ":" + t2._2)

val t3 = (1, 2, 3)
val t4 = (1, 2, 3, 4)
  • 配列(可変コレクション)
val a = Array(1, 2, 3, 4, 5)
val e = a(0) // 一番目にアクセス
a(0) = 100
println(a) // Array(100, 2, 3, 4, 5)
  • リスト(不変コレクション)
val l = List(1, 2, 3, 4, 5)
val e = l(0) // 一番目にアクセス
l(0) = 1 // コンパイルエラー
  • マップ(不変コレクション)
val m = Map(1 -> "a", 2 -> "b", 3 -> "c") // Map[Int, String]
val v = m(1) //: String "a"
val vOpt = m.get(1) //: Option[String] Some("a")

要素の繰り返し処理

val l = List(1,2,3) // Arrayも同じ

// for式
for(n <- l) println(n)

// 高階関数
l.foreach{ n => printl(n) }
l.foreach(println(_))
l.foreach(println)

val m = Map(1 -> "a", 2 -> "b", 3 -> "c")
for(t <- m) println(t) // t は (k,v)

m.foreach(println)

要素を検索する

val l = List(1, 2, 3)

l.find(_ == 2) // Option[Int]

val m = Map(1 -> "a", 2 -> "b", 3 -> "c")
m.find(_._1 == 1) // Option[(Int, String)]

要素の変換

val l = List(1, 2, 3)
val l2 = l.map(_ * 2) // List(2, 4, 6)

val m = Map(1 -> "a", 2 -> "b", 3 -> "c")
val m2 = m.map(e => (e._1, e._2 + e._1)) // Map(1 -> a1, 2 -> b2, 3 -> c3)
val m3 = m.map{ case (k, v) => (k, v + k) } // パターンマッチ機能を持つ、部分関数を使うともっと簡潔に記述できる

パターンマッチ

  • 数値のパターンマッチ
def numberMatch(n: Int) = n match {
  case 1 => println("one")
  case 2 | 3 => println("two or three")
  case _ => println("otherwise")
}

numberMatch(1) // one
numberMatch(2) // two or three
numberMatch(3) // two or three
numberMatch(4) // otherwise
  • match式は値を返す
def convertToString(obj: Any) = obj match {
  case n:Int => "one" // Int型の場合
  case "2" => "two" // 文字列の値の場合
  case List(3,3,3) => "three" // コレクションの場合
  case _ => throw new IllegalArgumentException
}

println(convertNumberToString(1)) // one
println(convertNumberToString("2")) // two
println(convertNumberToString(List(3,3,3)) // three
println(convertNumberToString(0.5)) // エラー
  • ケースクラスによるパターンマッチ
case class PersonName(firstName: String, lastName: String)
case class PostalAddress(zipCode: String, prefName: String, cityName: String, addressName: String, buildingName: Option[String])
case class Person(name: PersonName, postalAddress: PostalAddress)

val persons = List(Person(PersonName("Junichi", "Kato"), PostalAddress("000-0000", "東京都", "目黒区", "下目黒")), ...)

def hasLastNameAndPrefName(person: Person, lastName: String, prefName: String) = preson match {
  case Person(PersonName(_, l), PostalAddress(_, p, _, _, _) if l == lastName && p == prefName => true
  case _ => false
}

関数型プログラミング

  • FizzBuzz
for (n <- 1 to 100) {
  if (n % 15 == 0) {
    println("FizzBuzz")
  } else if (n % 3 == 0) {
    println("Fizz")
  } else if (n % 5 == 0) {
    println("Buzz")
  } else {
    println(n)
  }
}
(1 to 100).map({
  case n if n % 15 == 0 => "FizzBuzz"
  case n if n % 3 == 0 => "Fizz"
  case n if n % 5 == 0 => "Buzz"
  case n => n
}).foreach(println)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment