ja ja ja nb ja it ja ja ja ja ja en ja en en ja en ja en ja en ja en ja en ja en ja ja en ja en ja en en ja en ja en ja ja en ja en ja en ja en ja en ja en ja en ja en ja en en ja en ja en fa ja ja en nl ja en ja ja en en ja en ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja cv es es ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja ja
ところで2週間ほど前から、ふとしたきっかけで社内のエンジニアの方々と週一ペースの Scala 勉強会を始めることになりました。教材は twitter が公開しているScala Schoolです。僕が先週やった分のまとめ担当になったので、まとめを書いていこうと思います!先週は Scala School - Basics continued を一通りみんなで眺めました。
object Timer { var count = 0 def currentCount(): Long = { count += 1 count } }
クラスとオブジェクトは同じ名前を持てて、その場合 "Companion Object" と呼ばれる。Companion Object は主に下記のような感じで定義されることが多いらしい。
class Bar(foo: String) object Bar { def apply(foo: String) = new Bar(foo) }
Functions are Objects
関数は trait らしい。例えば引数を一つ取る関数は "Function1" という名前の trait を mixin したインスタンス。"Function1" は apply() を定義していて、オブジェクトを関数っぽく呼べる。
scala> object addOne extends Function1[Int, Int] { | def apply(m: Int): Int = m + 1 | } defined module AddOne scala> addOne(1) res2: Int = 2
def addOne(m: Int): Int = m + 1
と等価っていう認識で良いんだろうか。それとも「def キーワード使わなくても同じ事が出来るよ」ってこと?→コップ本読んだけど等価って言う認識でよさそう。
extends Function1[Int, Int] は extends (Int => Int) に省略可能らしいです。でもそもそも[Int, Int]が何なのか説明がないのでよくわかりません…Int を引数にとって Int を返すってことかな。
package は java っぽい感じ。object を使うと static function っぽく書ける。
package com.twitter.example object colorHolder { val BLUE = "Blue" val RED = "Red" }
上記のように定義すると、パッケージの外から下記のように呼び出せる。
println("the color is: " + com.twitter.example.colorHolder.BLUE)
Pattern Matching
Ruby で言う case when ですね。どうもcase whenとは違うらしい…!!今度調べます。
times match { case 1 => "one" case 2 => "two" case _ => "some other number" }
scala はパターンマッチング中に if を使うことで、複雑な条件をスマートに分岐できるっぽい。
times match { case i if i == 1 => "one" case i if i == 2 => "two" case _ => "some other number" }
パターンマッチング用の変数は自動で "i" になるみたい。上記のコードでは "i" を使っていますが、変数名に特に縛りは無いみたいです。x とか別の変数名にしても普通に動作しました。
上記の例だと if を使うメリットは特にないけど、もっと複雑な条件で恩恵を受けられそうな文法。
Matching on class members
Calculator というクラスのメンバを調べて場合分けするのを普通に書くと下記のようになる。
def calcType(calc: Calculator) = calc match { case calc.brand == "hp" && calc.model == "20B" => "financial" case calc.brand == "hp" && calc.model == "48G" => "scientific" case calc.brand == "hp" && calc.model == "30B" => "business" case _ => "unknown" }
後述する case class というものを使うともっとスマートに書けるらしい。
case classes
case class で宣言して、メソッド呼び出しのような形で case class のインスタンスを作る。
scala> case class Calculator(brand: String, model: String) defined class Calculator scala> val hp20b = Calculator("hp", "20b") hp20b: Calculator = Calculator(hp,20b)
case class は自動で同値判定用のメソッドと toString メソッドを持ってる。あと他の普通のクラスと同じようにメソッドを持たせることもできる。
case classes with pattern matching
case class を使ってパターンマッチングをしてみると下記のようになる。確かに見やすくなってる!
val hp20b = Calculator("hp", "20B") val hp30b = Calculator("hp", "30B") def calcType(calc: Calculator) = calc match { case Calculator("hp", "20B") => "financial" case Calculator("hp", "48G") => "scientific" case Calculator("hp", "30B") => "business" case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel) }
case の一番下は下記のようにも書ける
case Calculator(_, _) => "Calculator of unknown type"
Calculatorクラスのインスタンス以外もマッチングさせたかったら下記のように書ける
case _ => "Calculator of unknown type"
マッチした値に別の名前を付けて使うことも出来る。下記はマッチした Caluculator インスタンスに c という名前を付けて、戻り値となる文字列中で使ってる。
case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c)
例外の書き方はjavaと同じ。
try { remoteCalculatorService.add(1, 2) } catch { case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.") } finally { remoteCalculatorService.close() }
javaと違うのは、式として使うことも出来るということ。下記のような書き方はRubyでも時々使いますね。
val result: Int = try { remoteCalculatorService.add(1, 2) } catch { case e: ServerIsDownException => { log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.") 0 } } finally { remoteCalculatorService.close() }
Scala Schoolを使って勉強会を2回やってみて、この教材はおそらくScalaに詳しい人が近くに居る想定で作られているんだろうなと感じました。少なくともscalaを知らない初心者が一人でやるものじゃなさそう。勉強会ドリブンじゃなくて一人だったら続かなかっただろうなー。
なんとか最後までやりきって、簡単なwebアプリを作って公開できるくらい Scala をマスターできたらいいなと思います。
willnet 2011-11-16 16:59
Capybara の README 意訳
注意 この訳はだいぶ古い(2011年7月時のREADME)です。最新版の…
Capter 19 Cucumber Detail
rspec本一人読書会の続き。cucumberのお勉強メモです。 19.1 St…
メソッドとローカル変数はどちらが優先されるのか?
ruby内で、selfは基本的に省略可能なのですが、 self.hoge = "h…
« profile Ruby1.9.3ではKernel#randが少し便利になる »
id:willnet はてなブログPro
べんりで楽しいwebサービスをたくさん作りたい
読者です 読者をやめる 読者になる 読者になる
@netwillnetをフォロー
失敗したテストのログだけを出力するぞ
iCARE Dev Meetup #34で登壇しました
`Thread.current[:hoge]`はスレッドローカル変数を参照していると思いきや実際はファイバーローカル変数だった