15分でざっくり分かるScala入門
佐藤 祐一郎 JAIST IS 2014/6/21 kanazawa.rb meetup#22
1
自己紹介
2
 佐藤 祐一郎
 北陸先端科学技術大学院大学
情報科学研究科 博士後期2年
 人工知能作ってます
Scalaのいいところ
3
 JVM上で動く
(Javaのライブラリ
Java用のフレームワークなどに組み込める)
 オブジェクト指向+関数型
 きれいなJava
 個人的には困った時はJavaっぽく書けるし、Lispっぽくも
書けるし好き
Scala早見表 リテラル
4
Scala Java
Int int
Long long
Double double
Boolean boolean
String String
Tuple
Elem(xml)
その他Javaにあるものはある
val x : Int = 1
var str : String = “hoge”
val tuple = (1, “hoge”)
val xml = <xml><body>text</body></xml>
val 再代入不可
できるだけこっちを使う
var 再代入できる
できるだけ使わない
Scala早見表 制御構文
5
Scala Java
if (i < 10) { … } else { … } if (i < 10) { … } else { … }
while(i < 10){ … } while(i < 10) { … }
for(o : Object <- list) { … } for(Object o : list) { … }
n match {
case 1 => …
case 2 => …
case _ => …
}
switch(n) {
case 1 : … break;
case 2 : … break;
default : … break;
}
if文(式)は値を返す
val x = 1 + (if (true) 3 else 4)
->x : Int = 4
for文(式)も値を返せる
val list = for(i <- 1 to 10; if i % 3 == 0) yield i
->list = Vector(3,6,9)
Scala早見表 関数
6
Scala Java
def public
private def private
Unit void
return(省略可) return
コンパニオンオブジェクトで代用 static
override @Override
(arg0 : Type0, arg1 :Type1) => { … } (Type0 arg0, Type1 arg1) -> { … }
tlist : Type* Type… tlist
def inc(x : Int): Int = x + 1
private def positive(ls : List[Int]):List[Int] = {
ls foreach { print(_) }
ls filter (i => 0 < i)
}
Scala早見表 オブジェクト指向
7
Scala Java
class class
abstract abstract
trait(ただし実装を持てる!) interface
final final
extends extends
with implements
[A] <A>
[A <: B] A extends B
[A >: B] A super B
package package
import import
これくらい分かれば、ざっくりScalaは書ける
フィボナッチ数列
8
0, 1, 1, 2, 3, 5, 8, 13, …
0と1から始まって、あとは前の2つを足してできる数列
関数型プログラミングの入門でおなじみ
ヒマワリの種のつきかたとかが
フィボナッチ数列らしい
Javaっぽい実装
9
def fib(n : Int): BigInt = {
if (n <= 0) return 0
if (n == 1) return 1
var n1 = BigInt(0)
var result = BigInt(1)
for(i <- 2 to n) {
val tmp = result
result += n1
n1 = tmp
}
return result
}
これはn-1番目
これを
n番目
とすると
これはn-2番目
可読性 ?、 実行速度 ○
実行すると
for(i <- 0 to 100) print(fib(i)+",")
->0,1,1,2,3,5,8,13,21,34,55,…
関数型っぽい実装
10
def fib(n : Int): BigInt =
n match {
case n if n <= 0 => 0
case 1 => 1
case _ => fib(n - 1) + fib(n - 2)
}
fib(100) -> 終わらない
可読性 ◎、 実行速度 ×
nが0以下の場合ここにマッチ
nが0以下でも1でもないなら、前2つを足したもの
数学的な定義そのまま!
末尾再帰
11
def fib(n : Int): BigInt = {
if (n <= 0) return 0
def rec(i : Int, n1 : BigInt, result : BigInt):BigInt =
i match {
case i if n <= i => result
case _ => rec(i + 1, result, result + n1)
}
rec(1, 0, 1)
}
可読性 ○、 実行速度 ○
関数内で再帰関数を定義
カウンタ
ループを回りきったら即
計算結果を返す
関数に渡す前に計算しておく
varとかvalとかなくてもプログラムは書ける
オブジェクトプログラミング1
12
case class Person(val first_name : String,
middle_name : Option[String], last_name : String) {
def fullName: String =
middle_name match {
case Some(md : String) =>
first_name +" "+md+" "+last_name
case None => first_name+" "+last_name
}
def marryWith(person : Person): Person =
new Person(first_name, person.middle_name,
person.last_name)
}
Option型:nullの可能性がある値を包むモナド
caseクラス:equals()やhashCode()などを自動で作ってくれる
オブジェクトプログラミング2
13
object Person {
def apply(first_name : String, last_name : String): Person =
new Person(first_name, None, last_name)
def sasakiFamily(first_name : String): Person =
new Person(first_name, None, “Sasaki”)
}
val mika = Person.sasakiFamily(“Mika”)
コンパニオンオブジェクト:ここに定義された関数はstatic
ファクトリーみたいなもの
オブジェクトプログラミング3
14
val taro = Person.sasakiFamily("Taro")
val hana = Person("Hana", "Tanaka")
println(hana.fullName)
val sasaki_hana = hana marryWith taro
println(sasaki_hana.fullName)
->”Hana Tanaka”
“Hana Sasaki”
. とか () とか省略できる
15
Javaっぽく設計
関数型っぽく中身を実装
あとは
これでざっくりScala書ける
Javaには無い機能
16
クロージャ
17
def labelMaker(str : String): Unit => String = {
var i = 0
Unit => {
i += 1
str + i }}
val argMaker = labelMaker("arg")
for(i <- 1 to 3) print(argMaker()+",")
->arg1,arg2,arg3,
戻り値の型が関数
引数で与えられた文字列
クロージャ内に保存されている変数この
関数が
帰る
クロージャでフィボナッチ
18
def fib(n : Int): BigInt = {
val memo ={
var n1 = BigInt(1)
(x : BigInt) => {
var tmp = n1
n1 = x
tmp }}
var result = BigInt(0)
for(i <- 1 to n) result += memo(result)
result
}
引数で与えられた数を保存し、
前回保存した数を返す関数
関数の部分適用
19
def sandwich
(header : String)(fooder : String)(body : String): String =
header + body + fooder
def htmlMaker(tag : String)(body : String): String =
sandwich("<" + tag + ">")("</" + tag + ">")(body)
val commentout = sandwich("<!-- ")(" -->")_
val ptag = htmlMaker("p")_
println(commentout(“here is a text "))
List("hoge","goo","foo") map ptag foreach print
-><!– here is a text -->
<p>hoge</p><p>goo</p><p>foo</p>
3つ目の引数はまだ与えない
後から3つ目の引数を与える
何度でも使える
引数のカッコを区切る
部分適用でフィボナッチ
20
def fib(n : Int): BigInt = {
def add(n1 : BigInt)(n2 : BigInt): BigInt = n1 + n2
def rec(i : Int, add_memo : BigInt => BigInt,
result : BigInt): BigInt =
i match {
case i if n <= i => result
case _ => rec(i+1, add(result), add_memo(result))
}
rec(0, add(BigInt(1)), BigInt(0))
}
個人的にはこれが一番かな?
数をとってresultを足す関数
覚えていた数にresultを足す
暗黙の型変換
21
class ExtString(val str: String) {
def -(str2 : ExtString): ExtString = new ExtString(str.replace(str2.str, ""))
def *(str2 : String): List[String] =
for(s <- this.allSubstring; t <- new ExtString(str2).allSubstring)
yield s + t
def allSubstring: List[String] = { … }
override def toString(): String = str
}
object ExtString {
implicit def string2ExtString(str : String) = new ExtString(str)
}
import ExtString._
println("hoge" - "og")
"wnzk" * "a" filter(_.length == 2) foreach {print(_)}
->”he”
“ka””na””za””wa”
リテラルを自由に拡張できる!
Stringには – や * など無い
ExtStringにはあるから、
StringからExtStringへ変換する
変換規則を定義しておけばいい
文字列の全通りの組
無限リストでフィボナッチ
22
lazy val fib: Stream[BigInt] =
Stream.cons(0,
Stream.cons(1, fib.zip(fib.tail).map(p => p._1 + p._2)))
(0,1,1,2,3,5,8,…
無限リスト
(1,1,2,3,5,8,…
((0,1),(1,1),(1,2),(2,3),(3,5),(5,8),…
zip
タプルの要素を足す
(1,2,3,5,8,13… フィボナッチ数列
ちなみに ベンチマーク
23
1,000 10,000 100,000 500,000
Javaっぽい 5 20 223 4415
末尾再帰 3 4 182 4501
クロージャ 4 8 186 4401
部分適用 2 6 187 4703
無限リスト 28 69 OutOfMemoryError OutOfMemoryError
-Xmx 1024 -Xms 1024 でfib(n)を計算するのにかかる時間(ミリ秒)

15分でざっくり分かるScala入門

  • 1.
  • 2.
    自己紹介 2  佐藤 祐一郎 北陸先端科学技術大学院大学 情報科学研究科 博士後期2年  人工知能作ってます
  • 3.
  • 4.
    Scala早見表 リテラル 4 Scala Java Intint Long long Double double Boolean boolean String String Tuple Elem(xml) その他Javaにあるものはある val x : Int = 1 var str : String = “hoge” val tuple = (1, “hoge”) val xml = <xml><body>text</body></xml> val 再代入不可 できるだけこっちを使う var 再代入できる できるだけ使わない
  • 5.
    Scala早見表 制御構文 5 Scala Java if(i < 10) { … } else { … } if (i < 10) { … } else { … } while(i < 10){ … } while(i < 10) { … } for(o : Object <- list) { … } for(Object o : list) { … } n match { case 1 => … case 2 => … case _ => … } switch(n) { case 1 : … break; case 2 : … break; default : … break; } if文(式)は値を返す val x = 1 + (if (true) 3 else 4) ->x : Int = 4 for文(式)も値を返せる val list = for(i <- 1 to 10; if i % 3 == 0) yield i ->list = Vector(3,6,9)
  • 6.
    Scala早見表 関数 6 Scala Java defpublic private def private Unit void return(省略可) return コンパニオンオブジェクトで代用 static override @Override (arg0 : Type0, arg1 :Type1) => { … } (Type0 arg0, Type1 arg1) -> { … } tlist : Type* Type… tlist def inc(x : Int): Int = x + 1 private def positive(ls : List[Int]):List[Int] = { ls foreach { print(_) } ls filter (i => 0 < i) }
  • 7.
    Scala早見表 オブジェクト指向 7 Scala Java classclass abstract abstract trait(ただし実装を持てる!) interface final final extends extends with implements [A] <A> [A <: B] A extends B [A >: B] A super B package package import import これくらい分かれば、ざっくりScalaは書ける
  • 8.
    フィボナッチ数列 8 0, 1, 1,2, 3, 5, 8, 13, … 0と1から始まって、あとは前の2つを足してできる数列 関数型プログラミングの入門でおなじみ ヒマワリの種のつきかたとかが フィボナッチ数列らしい
  • 9.
    Javaっぽい実装 9 def fib(n :Int): BigInt = { if (n <= 0) return 0 if (n == 1) return 1 var n1 = BigInt(0) var result = BigInt(1) for(i <- 2 to n) { val tmp = result result += n1 n1 = tmp } return result } これはn-1番目 これを n番目 とすると これはn-2番目 可読性 ?、 実行速度 ○ 実行すると for(i <- 0 to 100) print(fib(i)+",") ->0,1,1,2,3,5,8,13,21,34,55,…
  • 10.
    関数型っぽい実装 10 def fib(n :Int): BigInt = n match { case n if n <= 0 => 0 case 1 => 1 case _ => fib(n - 1) + fib(n - 2) } fib(100) -> 終わらない 可読性 ◎、 実行速度 × nが0以下の場合ここにマッチ nが0以下でも1でもないなら、前2つを足したもの 数学的な定義そのまま!
  • 11.
    末尾再帰 11 def fib(n :Int): BigInt = { if (n <= 0) return 0 def rec(i : Int, n1 : BigInt, result : BigInt):BigInt = i match { case i if n <= i => result case _ => rec(i + 1, result, result + n1) } rec(1, 0, 1) } 可読性 ○、 実行速度 ○ 関数内で再帰関数を定義 カウンタ ループを回りきったら即 計算結果を返す 関数に渡す前に計算しておく varとかvalとかなくてもプログラムは書ける
  • 12.
    オブジェクトプログラミング1 12 case class Person(valfirst_name : String, middle_name : Option[String], last_name : String) { def fullName: String = middle_name match { case Some(md : String) => first_name +" "+md+" "+last_name case None => first_name+" "+last_name } def marryWith(person : Person): Person = new Person(first_name, person.middle_name, person.last_name) } Option型:nullの可能性がある値を包むモナド caseクラス:equals()やhashCode()などを自動で作ってくれる
  • 13.
    オブジェクトプログラミング2 13 object Person { defapply(first_name : String, last_name : String): Person = new Person(first_name, None, last_name) def sasakiFamily(first_name : String): Person = new Person(first_name, None, “Sasaki”) } val mika = Person.sasakiFamily(“Mika”) コンパニオンオブジェクト:ここに定義された関数はstatic ファクトリーみたいなもの
  • 14.
    オブジェクトプログラミング3 14 val taro =Person.sasakiFamily("Taro") val hana = Person("Hana", "Tanaka") println(hana.fullName) val sasaki_hana = hana marryWith taro println(sasaki_hana.fullName) ->”Hana Tanaka” “Hana Sasaki” . とか () とか省略できる
  • 15.
  • 16.
  • 17.
    クロージャ 17 def labelMaker(str :String): Unit => String = { var i = 0 Unit => { i += 1 str + i }} val argMaker = labelMaker("arg") for(i <- 1 to 3) print(argMaker()+",") ->arg1,arg2,arg3, 戻り値の型が関数 引数で与えられた文字列 クロージャ内に保存されている変数この 関数が 帰る
  • 18.
    クロージャでフィボナッチ 18 def fib(n :Int): BigInt = { val memo ={ var n1 = BigInt(1) (x : BigInt) => { var tmp = n1 n1 = x tmp }} var result = BigInt(0) for(i <- 1 to n) result += memo(result) result } 引数で与えられた数を保存し、 前回保存した数を返す関数
  • 19.
    関数の部分適用 19 def sandwich (header :String)(fooder : String)(body : String): String = header + body + fooder def htmlMaker(tag : String)(body : String): String = sandwich("<" + tag + ">")("</" + tag + ">")(body) val commentout = sandwich("<!-- ")(" -->")_ val ptag = htmlMaker("p")_ println(commentout(“here is a text ")) List("hoge","goo","foo") map ptag foreach print -><!– here is a text --> <p>hoge</p><p>goo</p><p>foo</p> 3つ目の引数はまだ与えない 後から3つ目の引数を与える 何度でも使える 引数のカッコを区切る
  • 20.
    部分適用でフィボナッチ 20 def fib(n :Int): BigInt = { def add(n1 : BigInt)(n2 : BigInt): BigInt = n1 + n2 def rec(i : Int, add_memo : BigInt => BigInt, result : BigInt): BigInt = i match { case i if n <= i => result case _ => rec(i+1, add(result), add_memo(result)) } rec(0, add(BigInt(1)), BigInt(0)) } 個人的にはこれが一番かな? 数をとってresultを足す関数 覚えていた数にresultを足す
  • 21.
    暗黙の型変換 21 class ExtString(val str:String) { def -(str2 : ExtString): ExtString = new ExtString(str.replace(str2.str, "")) def *(str2 : String): List[String] = for(s <- this.allSubstring; t <- new ExtString(str2).allSubstring) yield s + t def allSubstring: List[String] = { … } override def toString(): String = str } object ExtString { implicit def string2ExtString(str : String) = new ExtString(str) } import ExtString._ println("hoge" - "og") "wnzk" * "a" filter(_.length == 2) foreach {print(_)} ->”he” “ka””na””za””wa” リテラルを自由に拡張できる! Stringには – や * など無い ExtStringにはあるから、 StringからExtStringへ変換する 変換規則を定義しておけばいい 文字列の全通りの組
  • 22.
    無限リストでフィボナッチ 22 lazy val fib:Stream[BigInt] = Stream.cons(0, Stream.cons(1, fib.zip(fib.tail).map(p => p._1 + p._2))) (0,1,1,2,3,5,8,… 無限リスト (1,1,2,3,5,8,… ((0,1),(1,1),(1,2),(2,3),(3,5),(5,8),… zip タプルの要素を足す (1,2,3,5,8,13… フィボナッチ数列
  • 23.
    ちなみに ベンチマーク 23 1,000 10,000100,000 500,000 Javaっぽい 5 20 223 4415 末尾再帰 3 4 182 4501 クロージャ 4 8 186 4401 部分適用 2 6 187 4703 無限リスト 28 69 OutOfMemoryError OutOfMemoryError -Xmx 1024 -Xms 1024 でfib(n)を計算するのにかかる時間(ミリ秒)