関数型のパラダイムを学んで業務に活かそうということで、以下のドワンゴオリジナルの新卒エンジニア向けの研修資料でScalaを本格的に勉強してみることにした。
dwango.github.io
※Java8でごにょごにょしないのは、Scalaを趣味でやりたいという意図があるだけです。
どうせなら、Scalaの言語仕様からガッツリ学んで自分のものにしたい。
また、可能な限りGolangとの比較も入れていきます。
学んだことをとにかく走り書きしていきます。
構文(Syntax)
→そのプログラミング言語内でプログラムが構造を持つためのルールです。
「式(Expression)」
→プログラムを構成する部分のうち、評価することで値になるものです。
たとえば1や1 + 2、"hoge"などです。これらは評価することにより、数値や文字列の値になります。
「文(Statement)」
→式とは対照的にプログラムを構成する部分のうち、評価しても値にならないものです。たとえば変数の定義であるval i = 1は評価しても変数iが定義され、iの値が1になりますが、この定義全体としては値を持ちません。よって、これは文です。
・{}式
一般形
{ exp1; exp2; ... expN; }
{}式はexp1からexpNを順番に評価し、expNを評価した値
・制御構造
if式はJavaとは違い、値を返す。
if式は3項演算子のような形で使えるイメージ
object Expression { def main(args: Array[String]): Unit = { var age: Int = 5 var isSchoolStandard: Boolean = false println(if (isSchoolStandard && 1 <= age && age < 6) "幼児ではありません" else "幼児です") } }
while式
Unit型(Javaでいうvoid相当を返す)ので、Javaのwhile文とあまり変わらないイメージ。
for式
本当の力を理解するには、flatMap, map, withFilter, foreachといったメソッドについて知る必要があるとのこと。
1 to 10は1から10まで(10を含む)の範囲で、1 until 10は1から10まで(10を含まない)の範囲でループを回せる。
また、今自分がわかっているのは、コップ本に書かれていたyieldと組み合わせるとmap処理と同様の処理を実行できること。
多重ループみたいなことをしたい時にはmapメソッドを使うより便利とのこと(まだ聞いただけで書いてない)
→特にyieldキーワードを使ったfor式を特別に for-comprehensionと呼ぶことがあるそうです。
以下、サンプル。
scala> for(e <- List("A", "B", "C", "D", "E")) yield { | "Pre" + e | } res4: List[String] = List(PreA, PreB, PreC, PreD, PreE)
練習問題解答
scala> for (x <- 1 to 1000; y <- 1 to 1000; z <- 1 to 1000; if ((x * x) == (y * y + z * z))) println(x, y, z) (出力結果は省略)
match式
Javaのswitch文のようなイメージ。
JavaやJavaScriptのswitch文にあるようなフォールスルーはない。
scala> "abc" match { | case "abc" => println("first") // ここで処理が終了 | case "def" => println("second") // こっちは表示されない | } first
ちなみに、Golangもフォールスルーはありません。
Golangでフォールスルー動作を使用したい場合は、fallthroughステートメントを指定すると使用できる
k := 6 switch k { case 4: fmt.Println("was <= 4"); fallthrough; case 5: fmt.Println("was <= 5"); fallthrough; case 6: fmt.Println("was <= 6"); fallthrough; case 7: fmt.Println("was <= 7"); fallthrough; case 8: fmt.Println("was <= 8"); fallthrough; default: fmt.Println("default case") }
Javaでのswitch-case文は、int、enum、String(Java7から)にマッチすることができます。
Scalaのmatch式では、JavaのSwitchより強力で、コレクションの要素の一部にマッチさせる使い方があります。
scala> val lst = List("A", "B", "C", "D", "E") lst: List[String] = List(A, B, C, D, E) scala> lst match { | case List("A", b, c, d, e) => | println("b = " + b) | println("c = " + c) | println("d = " + d) | println("e = " + e) | case _ => | println("nothing") | } b = B c = C d = D e = E
また、Javaのswitch-case文は、定数式である必要がありますが、Scalaのmatch式もScalaの場合はそうでなくても処理できるようです。
scala> def count(n: Int, arg: Int) = n match { | case 1 => "one" | case 2 => "two" | case arg => "m" | } count: (n: Int, arg: Int)String scala> count(3,3) res8: String = m
ちなみに、GolangのSwitch文では文字列、数値やインタフェース変数の動的な型まで対応している。
また、Scalaと同様に、定数や整数である必要はありません。
実践Go言語(part5) - golang.jp
パターンマッチではガード式を用いて、パターンにマッチして、かつ、ガード式(Boolean型でなければならない)にもマッチしなければ右辺の式が評価されないような使い方もできます。
scala> val lst = List("A", "B", "C", "D", "E") lst: List[String] = List(A, B, C, D, E) scala> lst match { | case List("A", b, c, d, e) if b != "B" => | println("b = " + b) | println("c = " + c) | println("d = " + d) | println("e = " + e) | case _ => | println("nothing") | } nothing
また、パターンマッチのパターンはネストが可能です。先ほどのプログラムを少し改変して、先頭がList("A")であるようなListにマッチさせてみましょう。
scala> val lst = List(List("A"), List("B", "C", "D", "E")) lst: List[List[String]] = List(List(A), List(B, C, D, E)) scala> lst match { | case List(a@List("A"), x) => | println(a) | println(x) | case _ => println("nothing") | } List(A) List(B, C, D, E)
パターンの前に@がついているのはasパターンと呼ばれるもので、@の後に続くパターンにマッチする式を@の前の変数(ここではa)に束縛します。asパターンはパターンが複雑なときにパターンの一部だけを切り取りたい時に便利です。
→asパターン覚えよう。
型によるパターンマッチもあり、かなり強力な模様。
scala> import java.util.Locale import java.util.Locale scala> val obj: AnyRef = "String Literal" obj: AnyRef = String Literal scala> obj match { | case v:java.lang.Integer => | println("Integer!") | case v:String => | println(v.toUpperCase(Locale.ENGLISH)) | } STRING LITERAL
練習問題2つ目はこんな感じ。
for(i <- 1 to 1000) { val s = new scala.util.Random(new java.security.SecureRandom()).alphanumeric.take(5).toList match { case List(a,b,c,d,_) => List(a,b,c,d,a).mkString } println(s) }
Programming in Scala: Updated for Scala 2.12
- 作者: Martin Odersky,Lex Spoon,Bill Venners
- 出版社/メーカー: Artima Inc
- 発売日: 2016/05/10
- メディア: ペーパーバック
- この商品を含むブログを見る
Scala関数型デザイン&プログラミング―Scalazコントリビューターによる関数型徹底ガイド
- 作者: Paul Chiusano,Rúnar Bjarnason,株式会社クイープ
- 出版社/メーカー: インプレス
- 発売日: 2015/04/30
- メディア: Kindle版
- この商品を含むブログ (2件) を見る
- 作者: アンドリュー・フィリップス,ネルミン・セリフォヴィック,竹添直樹,島本多可子
- 出版社/メーカー: 翔泳社
- 発売日: 2016/02/05
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
- 作者: デイビッド・ポラック,羽生田栄一,大塚庸史
- 出版社/メーカー: 日経BP社
- 発売日: 2010/03/18
- メディア: 単行本
- 購入: 14人 クリック: 251回
- この商品を含むブログ (31件) を見る