Scala2.10の新機能を勉強する その2 ImplicitClass
前回のつづきで、
今日はScala2.10のImplicitClassをやってみたいと思います。
ImplicitClassって?
簡単に言うと、ImplicitConversionを楽に書ける機能、
と理解して良いんでしょうかね?
SIP-13 - Implicit classes - Scala Documentation
つまり
- 変換する型の定義
- 変換するメソッドの定義
が一発で書ける。
とりあえず、書いてみます。
ImplicitClassを書いてみる
こんな感じで書いてみました。
「HalfInt」はclassの定義とimplicit defの定義が必要ですが、
「DoubleInt」はimplicit classの定義だけで済んでいます。
これは確かに完結で良い。
ただ、これ、同じようにコンパイルされるんだろうか?
という事で、jadってみます。
package implicitclass; import scala.Function0; import scala.Predef$; import scala.runtime.*; // Referenced classes of package implicitclass: // ImplicitClass_01$ public final class ImplicitClass_01 { public static class HalfInt { public int half() { return i / 2; } private final int i; public HalfInt(int i) { this.i = i; super(); } } public static class DoubleInt { public int _mthdouble() { return i * 2; } private final int i; public DoubleInt(int i) { this.i = i; super(); } } public static class delayedInit.body extends AbstractFunction0 { public final Object apply() { Predef$.MODULE$.println(BoxesRunTime.boxToInteger($outer.DoubleInt(1)._mthdouble())); Predef$.MODULE$.println(BoxesRunTime.boxToInteger($outer.toHalfInt(1).half())); return BoxedUnit.UNIT; } private final ImplicitClass_01$ $outer; public delayedInit.body(ImplicitClass_01$ $outer) { if($outer == null) { throw new NullPointerException(); } else { this.$outer = $outer; super(); return; } } } public static void main(String args1[]) { ImplicitClass_01$.MODULE$.main(args1); } public static void delayedInit(Function0 function0) { ImplicitClass_01$.MODULE$.delayedInit(function0); } public static String[] args() { return ImplicitClass_01$.MODULE$.args(); } public static void scala$App$_setter_$executionStart_$eq(long l) { ImplicitClass_01$.MODULE$.scala$App$_setter_$executionStart_$eq(l); } public static long executionStart() { return ImplicitClass_01$.MODULE$.executionStart(); } public static HalfInt toHalfInt(int i) { return ImplicitClass_01$.MODULE$.toHalfInt(i); } public static DoubleInt DoubleInt(int i) { return ImplicitClass_01$.MODULE$.DoubleInt(i); } }
ほぼ同じようにコンパイルされてるっぽい(?)
ImplicitClassを切り出してみる
次にImplicitClassを切り出してみます。
コメントにも書いてありますが、
implicit classをトップレベルで書けないようです。
以下のようなエラーになりました。
[error] /Users/xxxx/workspace/scala-samples/scala-2.10-sample/src/main/scala/implicitclass/ImplicitClass_02.scala:10: `implicit' modifier cannot be used for top-level objects [error] implicit class TripleInt(i: Int) { [error] ^ [error] one error found [error] (compile:compile) Compilation failed
importに関しては、implicit defで定義する時も同じような使いかたをするだろうし、
あまり変わらないかな??
今日は、ここまでにします。
次回はValueClassに挑戦。
(内容薄いですが、メモなので、、、、(逃
Scala2.10の新機能を勉強する その1 StringInterpolation
久々のブログですが、
今更ながらScala2.10の新機能をちゃんと勉強しておこうと思います。
※終わったらRuby2.0やろうと思ってますが、、、
今回のテーマ
StringInterpolation
です。
StringInterpolationって?
Rubyの式展開のように、文字列リテラル内に、式を書く事ができる機能です。
Interpolationって、どういう意味だろうか、、、
初めて聞いたので、意味がわからず、、調べてみました。
alc
http://eow.alc.co.jp/search?q=interpolation&ref=sa
→〔他のものに〕挿入すること、差し挟むこと
なるほど、文字列になんか差し込むので、StringInterpolation、なんですね。
とりあえず言葉の意味は理解できました。
式を埋め込んでみる
まずは、単純に変数の値を埋め込んでみます。
これで、
Hello hoge!
となります。
Rubyの記法に比べると{}が無くて、#が$になった感じですね。
では、次に、計算式やらメソッド呼び出しやらを埋め込んでみます。
ここでは{}が必要な場面が登場しました。
変数もしくは引数なしのメソッド呼び出しは{}は不要ですが、
それ以外は必要になるようです。
※当然といえば当然ではある、、、。
フォーマットを指定して埋め込む
次はフォーマットを指定して値を埋め込んでみたいと思います。
ここで最後の行はエラーになりました。
コメントにも書きましたが、
piはDouble型になるので、Intを期待するフォーマット「%d」は使えません。
※コンパイル時にエラーになります
[error] /xxxx/workspace/scala-samples/scala-2.10-sample/src/main/scala/interpolation/StringInterpolation_03.scala:10: type mismatch; [error] found : Double [error] required: Int [error] println(f"$hoge is $pi%2.4d") // これはpiがDoubleなので、エラーになる [error] ^ [error] one error found [error] (compile:compile) Compilation failed
仕組みを知る
このStringInterpolationの仕組みはStringContextのメソッド飛び出しで実現されているようです。
試してみます。
こんな感じ。
hoge"...."
これは
StringContext(....).hoge(....)
に展開されるようなので、
ImplicitConversionと組み合わせれば、他にも便利なリテラルが実現できそうです。
他の機能を勉強してからやってみたいと思います。
Scala勉強日誌 - Akka その1
Scala勉強日誌 - Actor - 成らぬは人の為さぬなりけり
大分前にActorの勉強して、続きでAkkaの勉強しようと思ってて、完全に忘れていたので、
再開したいと思います。
(仕事で必要になって、勉強したので、メモしているだけ、、、)
今日のテーマは、、、
- SBTプロジェクトを作る
- AkkaのActorを書いてみる
- ActorからActorを呼び出して、結果を受け取ってみる
- Routerを使ってみる。
yagince/akka_practice · GitHub
例によって、環境は、、、
- OS:MacOSX10.8
- Scala : 2.10.0
- sbt :0.12.2
- Akka : 2.1.0
SBTプロジェクトを作る
まずは、プロジェクトを作ります。
今回は、全部ビルド定義ファイルは全部Scalaで書きたかったので、
こんな感じで作成
project/Build.scala
import sbt._ import Keys._ object BuildSettings { val buildOrganization = "yagince" val buildVersion = "0.0.1" val buildScalaVersion = "2.10.0" val buildSettings = Defaults.defaultSettings ++ Seq ( organization := buildOrganization, version := buildVersion, scalaVersion := buildScalaVersion ) } object Resolvers { val typeSafe = "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" val otherResolvers = Seq(typeSafe) } object Dependencies { val akkaCore = "com.typesafe.akka" %% "akka-actor" % "2.1.0" } object AkkaPracticeBuild extends Build { import Resolvers._ import Dependencies._ import BuildSettings._ val dependencies = Seq ( akkaCore ) val project = Project ( "akka-practice", file("."), settings = buildSettings ++ Seq ( resolvers := otherResolvers, libraryDependencies ++= dependencies ) ) }
project/build.properties
sbt.version=0.12.2
AkkaのActorを書いてみる
では、早速書いてみます。
src/main/scala/akka/sample/AkkaExample01.scala
package akka.sample import actor.PrintActor import akka.actor.{Props, ActorSystem} object AkkaExample01 extends App { val system = ActorSystem("sample") val actor = system.actorOf(Props[PrintActor], "hoge") actor ! "HelloWorld!" system.shutdown }
package akka.sample import actor.PrintActor import akka.actor.{Props, ActorSystem} object AkkaExample01 extends App { val system = ActorSystem("sample") val actor = system.actorOf(Props[PrintActor], "hoge") actor ! "HelloWorld!" system.shutdown }
src/main/scala/akka/sample/actor/PrintActor.scala
package akka.sample.actor import akka.actor.Actor class PrintActor extends Actor { def receive = { case x => println(x) } }
実行結果
> run-main akka.sample.AkkaExample01 [info] Running akka.sample.AkkaExample01 HelloWorld! [success] Total time: 0 s, completed 2013/02/13 22:16:22
単純に渡したオブジェクトを出力するだけのアクターです。
これだけ見ても、scala標準Actorより記述量が少ないですね。
そして、シンプル!
素晴らしい。
ActorからActorを呼んで結果を受け取ってみる
Scala標準Actorの同期メソッドみたいのあるのかな??
無さそう???と思っていたら、
こんな風にするのが普通なのかな???
というわけで書いてみます。
package akka.sample import akka.actor.{ActorSystem, Actor, Props} import akka.routing.RoundRobinRouter object AkkaExample02 extends App { val system = ActorSystem("sample") val actor = system.actorOf(Props[Master]) actor ! 100 Thread.sleep(500) system.shutdown } class Master extends Actor { val actor = context.actorOf(Props[DoubleActor]) def receive = { case i:Int => actor ! i case Doubled(x) => println("received : %d".format(x)) } } class DoubleActor extends Actor { def receive = { case i:Int => sender ! Doubled(i*2) } } case class Doubled(i:Int)
senderへ結果を返して、呼び出し側のreceiveへメッセージパッシングする感じですかね?
確かに、これはシンプルで綺麗だ。
よくよく考えると、同期メソッドって必要ないんじゃなかろうか、、、
Routerを使ってみる
AkkaにはRouterという機能があるようです。
Actorのインスタンスを管理して、
Routerにメッセージ送信すると、
Actorへよしなにバランシングしながらメッセージを横流ししてくれるような感じでしょうか?
書いてみます。
package akka.sample import actor.{Doubled, DoubleActor} import akka.actor.{ActorSystem, Actor, Props} import akka.routing.RoundRobinRouter object AkkaExample02 extends App { val system = ActorSystem("sample") val actor = system.actorOf(Props[Master]) (0 to 10).foreach(actor ! _) Thread.sleep(100) system.shutdown } class Master extends Actor { val router = context.actorOf(Props[DoubleActor].withRouter(RoundRobinRouter(2))) def receive = { case i:Int => router ! i case Doubled(x) => println("received : %d".format(x)) } }
> run-main akka.sample.AkkaExample02 [info] Running akka.sample.AkkaExample02 received : 0 received : 2 received : 6 received : 10 received : 4 received : 14 received : 8 received : 18 received : 12 received : 16 received : 20 [success] Total time: 0 s, completed 2013/02/13 22:27:41
一個前の例のMasterクラスを書き換えて、
DoubleActorをRouterで管理するようにしてみました。
Routerの種類は
- akka.routing.RoundRobinRouter
- akka.routing.RandomRouter
- akka.routing.SmallestMailboxRouter
- akka.routing.BroadcastRouter
- akka.routing.ScatterGatherFirstCompletedRouter
- akka.routing.ConsistentHashingRouter
Routing (Scala) — Akka Documentation
これだけあるようです。
それぞれの違いは、また次回ということで、、、
さて、今日はここまでにします。
CoffeeScriptを書いてみる その2
前回は、
関数定義、文字列内変数展開をやってみました。
さて、今回のテーマは、、、
- 可変長引数
- レンジ
- 比較演算子
- 条件付き代入
- 無名関数
をやってみたいと思います。
可変長引数
関数の引数に「...」をつけると可変長引数になります。
hoge = (ary...) -> console.log ary hoge 1,2,3,4,5
実行結果
[ 1, 2, 3, 4, 5 ]
ちゃんと配列で取得できているようですね。
レンジ
配列をレンジ(範囲)で作成することができます。
書き方はRubyとほぼ同じでした。
range = [1..10] console.log range
実行結果
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
逆順にすることもできます。
reverse_range = [10..0] console.log reverse_range
実行結果
[ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ]
「..」を「...」にすると、右端を含めないレンジになります。
※昇順でも降順でも同じ
reverse_range_2 = [10...0] console.log reverse_range_2
実行結果
[ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
比較演算子
等価比較
CoffeeSriptでは「==」を使うと「===」にコンパイルされます。
なので、==で型も含めた比較になります。
※CoffeeScriptにはJavaScriptでいう==に相当する比較演算子は無いそうです。
console.log 1 == "1"
実行結果
false
では、同じ事をJavaScriptでやってみます。
今回はNode.jsを使ってやってみます。
$ node > 1 == "1" true > 1 === "1" false
「1=="1"」はJavaScriptではtureになりますね。
「==」以外にも「is」という演算子が用意されています。
coffee> 1 is "1" false
※ファイルに書くのがめんどくさくなってきたので、辞めました。。。
「is」は「===」にコンパイルされるようです。
他にも自然言語でかける演算子がいくつか用意されています。
coffee> 1 is "1" false coffee> 1 isnt "1" true coffee> not true false coffee> not 1 false coffee> not undefined true coffee> not null true coffee> true and false false coffee> yes true coffee> no false coffee> false or true true
こういうのはコードが非常にリーダブルになるので好き。
存在確認をする演算子もあります。
coffee> hoge? false
RubyのActiveSupportのObject#tryのような使い方もできます。
coffee> hoge?.foo undefined coffee> hoge = {foo: "foo"} { foo: 'foo' } coffee> hoge?.foo 'foo'
条件付き代入
nullもしくは、undefinedの場合のみ代入する。
foo = null foo ?= "foo" console.log foo
実行結果
foo
※これはREPLからやると、エラーになる、、、
ちなみに、「?=」で代入する場合、変数「foo」は定義されている必要があります。
fooが未定義の場合
Error: In foo.coffee, the variable "foo" can't be assigned with ?= because it has not been defined.
と怒られました。
falseの時のみ代入する
bar = false bar ||= "bar" console.log bar
実行結果
bar
ちなみに、これもbarが定義されていないと怒られます。
これは、Rubyと同じで、条件的にfalseと判定できればなんでもいけるのか??
というわけで試してみます。
a = false a ||= "Yes" console.log a b = 0 b ||= "Yes" console.log b c = null c ||= "Yes" console.log c d = undefined d ||= "Yes" console.log d e = "" e ||= "Yes" console.log e
実行結果
Yes Yes Yes Yes Yes
なるほど。
変数が定義されてさえいれば、あとはRubyと同じようなノリなのかな。
変数が定義されていない場合に代入する、というのは、こんな感じで書くんでしょうか?
coffee> hoge = hoge ? "hoge" 'hoge'
無名関数
最後に、無名関数を関数の引数に渡す場合の書き方です。
基本的には、関数定義時となんらかわりありません。
setTimeout -> console.log "Hoge" , 1000
実行結果
$ coffee nameless.coffee Hoge
一秒待って、Hogeと表示されます。
ちなみに、当たり前な事ではありますが、こう書くと、意図しない動きになります。。。
setTimeout -> console.log "Hoge" , 1000
実行結果
$ coffee nameless.coffee Hoge 1000
setTImeoutに対して、
console.log("hoge",1000)
を呼び出す関数を渡した事になるので、当たり前ではありますが、
最初ちょっと???になりました。
(インデントに慣れていないので、、、)
今日はここまで。
次は配列の操作とかクラス定義とかやろうと思ってます。