@wataru420 | amebaId:wataru420
Cyberagent Application Engineer
Scalaコップ本輪読会用の資料です。
Slides available at http://goo.gl/SVfmP
I use this: github.com/kig/BasicsOfThreeJS
コップが溢れる程
Scalaを愛したい
Scalaの名前の由来
ユーザーの求めに応じて成長できるよう設計
Javaプラットフォーム上で動くので間口も広い
Scalaはオブジェクト指向と関数型プログラミングの概念を、静的な型付けを行う言語にまとめ上げたものである。
みんなでScalaを使えるかっこいいエンジニアになろう!!
var capital = Map("US" -> "Washington", "France" -> "Paris") capital += ("Japan" -> "Tokyo") println(capital("France"))
Perl、Python、Rubyなどのスクリプト言語のような感じ
連想マップは便利
でも、フリーサイズで誰でも着られるみたいなのは嫌だ
さっきのをHashMapに書き換えてみます
import scala.collection.imutable.HashMap var capital = HashMap("US" -> "Washington", "France" -> "Paris") capital += ("Japan" -> "Tokyo") println(capital("France"))
簡単ですね
さっきのをTreeMapに書き換えてみます
import scala.collection.immutable.TreeMap var capital = TreeMap("US" -> "Washington", "France" -> "Paris") capital += ("Japan" -> "Tokyo") println(capital("France"))
簡単ですね
SynchronizedMapトレイトでスレッドセーフに
import scala.collection.mutable.{ HashMap, SynchronizedMap } var capital = new HashMap[String,String] with SynchronizedMap[String,String] capital += ("US" -> "Washington", "France" -> "Paris") capital += ("Japan" -> "Tokyo") println(capital("France"))
このようにScalaは柔軟で書きやすく、そして必要に応じて細かく修正していける
Scalaは型を簡単に作れる
Scalaは型を使った条件分岐ができる
Scalaは型に関する機能が豊富
型に積極的な意味をもたせられる
メソッドやメンバ変数を束ねるだけのものではない!
javaのBigIntegerはint型とはだいぶ異なる
ScalaではこのBigIntegerをラップしたBigIntクラスがある
*などの演算子があたかも予約語かのように使える
Caseクラスとパターンマッチを使うと、
型に意味を持たせられる
def eval(e: Expr): Int = e match { case Number(x) => x case Sum(l, r) => eval(l) + eval(r) }
Scalaのパターンマッチはかなり強力
recipient ! msg val recipient = actor { loop { recive{ case Msg1 => … //Msg1の処理 case Msg2 => … //Msg2の処理 // … } } }
ScalaはErlangのアクターモデルを実装している
メッセージ送信(!)やactor、loop、receive
いずれも組み込み演算ではない
Scalaのアクターライブラリーで定義されている
Scalaの中で新しい言語を作ることすら可能
興味があったらScalaでパーサーを作ってみるを見てね
これはリフレクションを複雑にするなど、
スケーラビリティを下げている。
純粋なオブジェクト指向じゃないよね
1 + 2
+ は演算子に見えるが、Intオブジェクトのメソッド
Traitはメソッドとフィールドをカプセル化したもの。
Traitを使うとミックスインのような事が可能となる。
//普通の関数 def 関数名(引数:型, …) = 本体 //関数リテラル (引数:型, …) => 本体 //こんな風に使える args.foreach(arg => println(arg))
Scalaは完璧ではない
イミュータブルなデータ構造多く用意している
つまり副作用のないメソッドを容易に作れる
ScalaはJavaのライブラリーを多用している。
ScalaからJavaを使うために特別な構文などはない。
実際MyBatisとかもさくっと使えました。
//これはJava class MyClass { private int index; private String name; public MyClass(int index, String name) { this.index = index; this.name = name; } }
// Scalaならこれだけ class MyClass(index: Int, name: String)
行数が減れば入力が楽、そして理解する労力も減る?
例えばStringの変数の中に大文字があるかどうかを知る関数
// Java boolean nameHasUpperCase = false; for (int i = 0; i < name.length(); ++i) { if (Character.isUpperCase(name.charAt(i))) { nameHasUpperCase = true; break; } }
val nameHasUpperCase = name.exists(_.isUpperCase)
文字列をより高い水準の存在として扱い、
複雑さを緩和している。
Javaでも似たような制御構造を作ることは可能だが面倒。
関数リテラルはプログラムは短くてキレイなものとする。
静的型付け言語は一般的には冗長だと言われている
型を指定しないとかけない。
コーディング量が増える。
Scalaでは強力な型推論があるので簡潔に書ける。
型が静的である事自体が制限になる。
将来の変化の足かせになる。
Scalaではパターンマッチングと強力な合成の力で
かなり柔軟に書ける。
真偽値が整数に加算されていない
文字列の集合に文字列以外が追加されない
コンパイル時に検出可能
テストで証明できるのはエラーの存在だけ
エラーの不在ではない!
コンパイル時に検出されるエラーがリファクタリング時のセーフティネットとなる。
影響範囲の特定が楽
def f(x: String) = …
fの引数がStringでなければならないことがわかる。
ただ、これはやだ
val x: HashMap[Int, String] = new HashMap[Int, String]()
val x = new HashMap[Int, String]() val x: Map[Int, String] = new HashMap()
型推論してくれるScalaならこれでいい
$scala scala> 1 + 2 res0: Int = 3
計算された値を参照する自動生成またはユーザー定義で作られた名前
コロンと式の型
等号
式評価の結果
res0は実際に使える
scala> res0 * 3 res1: Int = 9
みんなの大好き「Hello world」
scala> println("Hello, world!") Hello, world!
val定義
scala> val msg = "Hello, world!" msg: java.lang.String = Hello, world!
型を明示的に指定したいときは
scala> val msg2: java.lang.String = "Hello, world!" msg2: java.lang.String = Hello, world! scala> val msg3: String = "Hello, world!" msg3: String = Hello, world!
こんな風に使えます
scala> println(msg) Hello, world!
valは定数、Javaでいうfinal
scala> msg = "Goodbye cruel world!" <console>:8: error: reassignment to val msg = "Goodbye cruel world!"
varが変数
scala> var greeting = "Hello, world!" greeting: java.lang.String = Hello, world! scala> greeting = "Leave me alone, world!" greeting: java.lang.String = Leave me alone, world!
複数行にまたがる入力は、うまいことやってくれる
scala> val multilLine = | "This is next line." multilLine: java.lang.String = This is next line.
縦棒(|)が表示されて入力を続けられる。
間違えたら[Enter]2回
scala> val oops = | | You typed two blank lines. Starting a new command.
scala> def max(x: Int, y: Int): Int = { if (x > y) x else y } max: (Int, Int)Int
def 関数名(引数:型, …) = 本体)
関数が再帰的な場合などは結果型の指定が必要だが、今回は省略できる。
scala> def max2(x: Int, y: Int) = if (x > y) x else y
結果を返さない関数もある
scala> def greet() = println("Hello, world!") greet: ()Unit
Utin型はJavaのvoid型みたいなもの
終了は
scala> :quit $
hello.scala
println("Hello, world, from a script!")
$ scala hello.scala
もちろん引数も渡せる。
helloarg.scala
println("Hello, " + arg(0) + "!")
$ scala helloarg.scala planet
printargs.scala
var i = 0 while (i < args.length) { println(args(i)) i += 1 }
このプログラムはあまり良くないがとりあえずwhileの説明
echoargs.scala
var i = 0 while (i < args.length) { if (i != 0) print(" ") println(args(i)) i += 1 } println()
このプログラムはあまり良くないがとりあえずwhileの説明
foreachを使おう
args.foreach(arg => println(arg))
args.foreach((arg: String) => println(arg))
arg.foreach(println)
for (arg <- args) println(arg)
しかしfor式はこんなもんじゃない
詳しくは7.3節と第23章で
Javaでいうジェネリクス
val greetStrings = new Array[String](3) greetStrings(0) = "Hello" greetStrings(1) = ", " greetStrings(2) = "world¥n" for (i <- 0 to 2) print(greetStrings(i))
丁寧に書くと
val greetStrings: Array[String]= new Array[String](3)
greetStrings(0)
valの中身を書き換えている
0 to 2
内部的には
(0).to(2)
パラメータが1つならドットと括弧を省略できる
greetString(0) = "Hello" greetStrings.update(0, "Hello")
val numNames = Array("zero", "one", "two") val numNames2 = Array.apply("zero", "one", "two")
上下で同じ意味
applyはArrayコンパニオンオブジェクトで定義されている
詳しくは4.3章
Array[String]は中身が変更できるからミュータブル
ScalaのListはイミュータブル
val oneTwo = List(1, 2) val threeFour = List(3, 4) val oneTwoThreeFour = oneTwo ::: threeFour println(" " + oneTwo + " and " + threeFour + " were not mutated.") println("Thus, " + oneTwoThreeFour + " is a new list.")
val twoThree = List(2, 3) val oneTwoThree = 1 :: twoThree println(oneTwoThree)
メソッド名末尾がコロンの場合は右被演算子
val pair = (99, "Luftballons") println(pair._1) println(pair._2)
1から始まるのは静的に型付されたタプルの伝統
var jetSet = Set("Boeing", "Airbus") jetSet += "Lear" println(jetSet.contains("Cessna"))
内部的にはこうなってる
jetSet = jetSet + "Lear"
import scala.collection.mutable.Set val movieSet = Set("Hitch", "Poltergeist") movieSet += "Shrek" println(movieSet)
import scala.collection.immutable.HashSet val hashSet = HashSet("Tomatoes", "Chilies") println(hashSet + "Coriander")
import scala.collection.mutable.Map val treasureMap = Map[Int, String]() treasureMap += (1 -> "Go to island.") treasureMap += (2 -> "Find big X on ground.") treasureMap += (1 -> "Dig.") println(treasureMap(2))
下の二つは同じ意味
1 -> "Go to island." (1).->("Go to island.")
(1,"Go to island.")というタプルを返す
val romanNumeral = Map ( 1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV", 5 -> "V" ) println(romanNumeral(4))
def printArgs(args: Array[String]): Unit = { var i = 0 while (i < args.length){ println(args(i)) i += 1 } }
どうする?
def printArgs(args: Array[String]): Unit = { for (arg -> args) println(arg) }
まだいける
def printArgs(args: Array[String]): Unit = { args.foreach(println) }
真の関数型は意味のある何かを産み出す
def formatArgs(args: Array[String]) = args.mkString("¥n") println(formatArgs(args))
副作用を持つコードを最小化することでテストしやすく
ただ、varが正しい場合もあるのでそのへんはバランス
import scala.io.Source if (args.length > 0) { for (line <- Source.fromFile(args(0)).getLines) println(line.length + " " + line) } else Console.err.println("Please enter filename")
22 | import scala.io.Source 0 | 22 | if (args.length > 0) { 49 | for (line <- Source.fromFile(args(0)).getLines) 39 | println(line.length + " " + line) 1 | } 5 | else 46 | Console.err.println("Please enter filename")
import scala.io.Source def widthOfLength(s: String) = s.length.toString.length if (args.length > 0) { val lines = Source.fromFile(args(0)).getLines.toList var maxWidth = 0 for (line <- lines) maxWidth = maxWidth.max(widthOfLength(line)) for (line <- lines) { val numSpaces = maxWidth - widthOfLength(line) //プリミス val padding = " " * numSpaces println(padding + line.length + " | " + line) } } else Console.err.println("Please enter filename")
import scala.io.Source def widthOfLength(s: String) = s.length.toString.length if (args.length > 0) { val lines = Source.fromFile(args(0)).getLines.toList val longestLine = lines.reduceLeft( (a, b) => if (a.length > b.length) a else b ) val maxWidth = widthOfLength(longestLine) for (line <- lines) { val numSpaces = maxWidth - widthOfLength(line) val padding = " " * numSpaces println(padding + line.length + " | " + line) } } else Console.err.println("Please enter filename")
Wataru Fukunaga
@wataru420
| amebaId:wataru420
Slides available at http://goo.gl/SVfmP