15分でざっくり分かるScala入門
description
Transcript of 15分でざっくり分かるScala入門
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)を計算するのにかかる時間(ミリ秒)