
@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