Groovy物語 Twitter

2011-09-02

Story #13 ツキのマップ

「というわけで、お前にマップの使い方を教えてやってくれって、おじさんから頼まれたんで今日は俺がじっくり教えてやろう!」
「ねぇ、あんたっていつも思うんだけど会話の仕方を知らないの?主語がないから何がなんだかわからないのよ」
「はぁ?Groovyの話に決まってんだろ?ったく…これだから引き受けたくなかったんだよな」
「うるさいなぁ、何なのそれ!なんで私があんたなんかにイヤイヤ教えてもらわなきゃならないわけ??」
「お前はGroovyのこと、もっといっぱい覚えたくないのかよ」
「え…?そりゃぁ覚えたいけど」
「じゃあ、つべこべ言わずに…い、一緒にマップ…覚えてみない?」
「へ…?な…なんなのよ!調子狂うなぁ…わかったわよ、一緒にやってあげてもいいわよ」
「…よーし!それじゃあまずは空マップからだ」

[:]

「これが空マップだ、空って言うくらいだから、何も入ってないマップだな」
「ちょっと待ってよ、空とかそういう話の前にマップってなんなのよ?ダンジョンのマップとか?」
「(はぁ?これだから…)ええと…そうか。まずはマップっていうのが何かってところから、か」
「い・や・な・ら・教えてくれなくても、いいのよ?」
「まぁ、そう怒るなよぉ…」
「…!」
「よ、よし、じゃあ続きやるぞ」
「まずはマップっていうのは何かってところだけど、お前リストは知ってるんだよな?」
「ええ、知ってるわよ」
「よしよし、じゃあリストとの違いで説明するかな。例えばこんな“ツキ”のリストがあったとする」

def tsukiList  = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']

「喧嘩売ってんの?」
「(スルー)さて、例えば '3月' っていう文字列アクセスしたい場合、お前ならどうする?3月、つまり美月だぞ」
「(ムカーっ!)ううーーー!そんなの簡単じゃん!3番目なんだから」

assert tsukiList[2] == '3月'

「でしょ?」
「ほほー、ゼロオリジンにひっかからなかったな」
「あったりまえじゃん!からかわないでよ」
「へへーお許しを、ミツキ様(笑)」
「で!なんなのよそれが」
「まてまて、じゃあ次の場合は?」

def tsukiList2 = ['6月', '10月', '5月', '12月', '9月', '11月', '3月', '1月', '7月', '8月', '4月', '2月']

「ええと…、7番目だから…」

assert tsukiList2[6] == '3月'

「どうよ」
「正解!」
「ただ並び替えたじゃん!これがなんなのよ?」
「お前ってほんとにせっかちだなぁ…それじゃあお待ちかねのマップに行くぞ。上のtsukiListをマップにするとこんな感じになる」

def tsukiMap = [
  jan: '1月',
  feb: '2月',
  mar: '3月',
  apr: '4月',
  may: '5月',
  jun: '6月',
  jul: '7月',
  aug: '8月',
  sep: '9月',
  oct: '10月',
  nov: '11月',
  dec: '12月'
]

「長くなるのね」
「違うそこじゃないって、見にくいから適度に改行を入れてやっただけだよ… 注目するのは各月の前の“英語:”の部分だ」
「この英語って各月の英語の頭文字よね?」
「そうだ、それで、美月にアクセスするには、こう書くんだ」

assert tsukiMap.mar == '3月'

「へぇ〜!ドットと英語でアクセスできるんだ」
「英語って言ってるところはキー(key)、各月の方がバリュー(value)って言い方をするのが一般的だ。だから、この例だと 'mar' って文字列がキーで、'3月' がバリューになるってわけだな」
「ふーん、ところで、キーっていうのは文字列なのに '' で囲まなくていいの?」
「ああっ、文字列の場合省略できるんだよ、別に 'mar' って書いても意味は同じだな」
「意味は同じだけど、簡単な書き方…ってことはシンタクスシュガーね!」
「お、その単語も知ってるのか、おじさんお前に色々教え込んでるんだなぁ」
「ええ!マスターはやさしいもん!」
「はいはい、お前年上がそんなに好きなのか」
「そ、そういうんじゃないもん!」
「まぁどうでもいいや、それでな、マップにしとくとさっきの順番がぐちゃぐちゃになったリストみたいな場合でも結果が変わらないんだよ。こんな感じでな」

def tsukiMap2 = [
  jun: '6月',
  oct: '10月',
  may: '5月',
  dec: '12月',
  sep: '9月',
  nov: '11月',
  mar: '3月',
  jan: '1月',
  jul: '7月',
  aug: '8月',
  apr: '4月',
  feb: '2月'
]
assert tsukiMap2.mar == '3月'

「それから、キーとバリューの組み合わせが合ってれば全く同じマップとみなされるから」

assert tsukiMap == tsukiMap2

「も成立するんだぞ。順番は関係ないんだ」
「へぇ〜、そうなんだぁ。なるほどなるほどねぇ、面白い!」
「おっとヤバイ、そろそろ休み時間終わっちまうな。とにかくマップっていうのはキーを使ってアクセスできる便利なものって覚えておけばいいんだ、分かったか?」
「分かったわよ!なんで上から目線なのよ」
「ハハハ、よーし、それじゃあ続きはまた放課後な」
「またね」

2011-08-08

Story #12 男同士の約束

「やぁおじさん、久しぶり」
「おーユウスケ、来たか、さぁ入って入って」
「どうしたの?急に。おじさんから呼んでくれるなんてめずらしいよね」
「あー、いやいや、ちょっとね。それはそうと、お前、アイスとホットどっちがいい?」
「この暑いのにホットはないって!アイスアイス!」
「ははは、アイスだな、わかったよ。まぁ、ゆっくりしてから話をするとしようか」


「それでな、お前のクラスにツキちゃんって子がいるだろ?」
「へ?、ああ、ツキね、いるけど? そういえばツキのやつ、最近ここによく遊びに来てるんだってね」
「ああ、よく遊びにくる。あの子は素質があるかもしれないなって思ってね、Groovyを教えていても楽しいんだよ」
「え?あいつに素質が? …そうは見えないけどねぇ、だってあいつ、リストも知らなかったんだぜ?」
「まだGroovyの勉強をはじめたばかりだからね、あの子も。でもお前が九九の問題を出してくれたおかげでだいぶツキちゃんも上達したよ」
「それはそれは、よかったでございますねー」
「あれ?どうしたんだユウスケ、なんだかふてくされてないか?」
「ち、違う!そんなわけないだろ!からかわないでよおじさん」
「ははは、まぁ、僕はどっちでもいいんだけどね」
「それはそうと、なんで俺を今日呼んでくれたの? ツキの話をしたかったから?」
「正解」
「いやなこった、あいつ、よくわかんねーけど、俺にすぐ敵対心むき出しにして来るんだぜ?こないだなんて平手打ちされるし、たまったもんじゃないぜ、まったく」
「それはお前がツキちゃんをレイディとして扱っていないからだろ?好きな女の子には優しくしなきゃだめだぞ」
「は、はぁ?なんだよそれ、だれがあいつのことなんか…好きって」
「ははは、図星っぽいな」
「…(くぅーー!)…」
「まぁ、そんなわけで、君たちはせっかくクラスメイトなんだし、共にGroovyを学んで欲しいわけだ。差し当たって、ツキちゃんにマップについて教えてあげて欲しい」
「なんだか丸め込まれた気がするなぁ…」
「まぁまぁ(笑)僕に頼まれたって言えば、教えやすくなるだろう?どうだ?やってみないか?」
「おじさんがそこまで言うなら…、勘違いしないでくれよ、おじさんが言うからやってやるだけだからな」
「よし、それじゃあ利害が一致したところで」


「お前、マップはどのくらい知ってる?」
「マップなら得意だよ、一通り知ってるさ」

[a: 1, b: 2, hello: 'world'].each{
  println "${it.key} = ${it.value}"
}

結果:

a = 1
b = 2
hello = world

「やっぱりマップっていったら、キーバリューをイーチするのが快感だよね、おじさん」
「いきなり難しいところからじゃなくて、丁寧にしっかり教えてやってくれよ、たとえば空のマップとか」

[:]

「え、そんな面倒なところからかよ?」
「そうだっ。基本からじっくりとだ。じゃあ成果を楽しみにしてるならな」
「ちっ…わかったよ、おじさん、それじゃあまたね」

2011-07-31

Story #11 Do you wanna stay with me?

「今日はあんまり時間がないからさらっと行くよ」
「はぁーい、さらっと行くよーでお願いします」
「そこ、僕のものまねは要らないからね」
「えへっ」

「それじゃあ、withについてだけど、withっていう単語は「一緒に」って意味なのは分かるかな?」
「うん」
「よしよし、withって言うのは簡単で、自分自身をクロージャに渡して、処理を引き継いでもらってる感じって覚えればいいかな。まずはサンプルを」

'Do you wanna stay'.with { me ->
  println me // => 'Do you wanna stay'って表示される
}

「こんな感じかな?ちょっとあまりいいサンプルじゃないかもしれないんだけど、'Do you wanna stay' っていうのが文字列オブジェクトだよね?で、withを使ってmeっていう名前で、クロージャの中で参照できるようにしてるのさ」
「うん、なんとなく分かったけど、これって何か意味があるの? たとえばさ」

println 'Do you wanna stay'

「じゃ、だめなの??」
「うん、それでいいよ」
「え?」
「ごめんね、だからこれはちょっと例が悪い(笑)実際はもっと同じオブジェクトに対して連続に処理をさせたい場合とか、流れるようなインタフェース*1にしたい場合に使える方法だね、さっきのhappybirthday.groovyに戻ってみようか」

happybirthday.groovy
import javax.sound.midi.*

def midi = args[0]
MidiSystem.sequencer.with {
  open() // <= ここ
  setSequence(MidiSystem.getSequence(getClass().getResource(midi))) // <= ここ
  println "Play ${midi}."
  start() // <= ここ
  while (isRunning()) { Thread.sleep(1000) }
  close() // <= ここ
  println "Stop ${midi}."
}

「元がこれね、コメントでここってしたところがwithのおかげで省略して書けてるところだよ」
「いきなりおーぷん!とかくろーず!とかしちゃってるところね」
「そうそう、で、次はwithを使わなかったらどうなるか」

happybirthday.groovy(withなし)
import javax.sound.midi.*

def midi = args[0]
def seq = MidiSystem.sequencer
seq.open() // <= ここ
seq.setSequence(MidiSystem.getSequence(getClass().getResource(midi))) // <= ここ
println "Play ${midi}."
seq.start() // <= ここ
while (isRunning()) { Thread.sleep(1000) }
seq.close() // <= ここ
println "Stop ${midi}."

「わぁ…毎回seq.が出て来る><」
「ははは、ね?こんな風に全部書くのは大変だし、コードも読みにくくなっちゃうだろう?どこまでがseqがカバーすればいい領域なのかも分かりづらいし。コードは読みやすくあるべきなんだ。コードの読みやすさのことをコードの可読性って呼んだりするね」
「かどくせい」
「そうそう、ツキちゃんのいいところはそうやってちゃんと繰り返してみて、自分も理解を確かめるところだね」
「えっ、それいいところなの??」
「うん、いいところだよ。そうやって、ちゃんと私は理解した、って確かめることで、より理解が深まるんだ」
「ふぅーん、何も考えずにずっとしてたことだよ…大したことじゃないし…でも褒められるとなんだか照れちゃう、マスターだからかな」
「え?あ、そんな照れられても(苦笑)、あと、大事なことが一つ。さっきはクロージャの中で me -> ってやってたと思うけど、あれは省略できて、itを使うこともできるんだよ」

'Do you wanna stay'.with {
  println it // => 'Do you wanna stay'って表示される
}

「あと、誤解を招かない範囲であればitやme自体を省略することもできるんだ。だから」

'Do you wanna stay'.with { me ->
  println toUpperCase() // => 'DO YOU WANNA STAY'って表示される
}
'Do you wanna stay'.with {
  println toUpperCase() // => 'DO YOU WANNA STAY'って表示される
}

「実はこれでもオッケーなんだよ」
「ふぅーん、そうなんだ」
「元々、省略してすっきり書きたいための方法だからね。なるべく短く書けるようにってGroovyが裏で努力してる姿が見えるよね」

「はーい。ところでさ、マスター。どぅゆわなすていうぃずみー?ってどういう意味なの?」
「え、あ、ええと、それは自分で意味を調べるように!」
「えーー教えてよぅ><」
「だーめ(笑)ほら、もう今日は遅いから帰った帰った。また今度ね」
「は、はぁーい…またね!マスター」

*1:obj.with{}.with{}.with{} と処理を数珠繋ぎにしてゆくことが可能。

2011-07-21

Story #10 あなたとわたしをつなぐもの

「こんにちっ…」

「いらっしゃい!おめでとう!ツキちゃん!」
「(おめでとーー!おめでとーー!)」
「(ザワザワザワ)」

「わぁーっ、みんな…なんでもういるの?」
「ツキが今日は誕生日だっていうからサプライズだよ!」
「えっ、わー、うれしいよ、ありがとう!」
「でもさっきまでみんな学校にいたのにどうして??」
「ダッシュに決まってる(笑)」

「さぁ、ツキちゃん座って座って」
「はーい、そんなに急かさなくても座るよ」
「ツキちゃん、これは僕からのプレゼントコードだよ!」

happybirthday.groovy
import javax.sound.midi.*

def midi = args[0]
MidiSystem.sequencer.with {
  open()
  setSequence(MidiSystem.getSequence(getClass().getResource(midi)))
  println "Play ${midi}."
  start()
  while (isRunning()) { Thread.sleep(1000) }
  close()
  println "Stop ${midi}."
}

「これをこんな風に実行してみて」*1

$ groovy happybirthday.groovy birthday.mid

「やってみるね。パチパチパチ…パン(Enter押下音)」

(しばらくして…)

「わぁ〜!きれいなオルゴールの音色。すごいすごい!ありがとう」

「うぁ〜すごいね、これもGroovyで演奏してるの?」
「そうだよ、事前にMIDIファイルは用意してあるんだけどね。でもGroovyだったらさっき示したように凄く短いコードでこんなこともできてしまうんだよ」
「さすがCafe Groovyのマスターさんだけのことはあるね」
「みんな僕を褒めてる場合じゃないよ、今日の主役はツキちゃんだ」

「さぁ、ツキちゃんのために用意したケーキだよ!」
「わぁ〜〜ありがとう!マスター、それにみんな、ほんとうにどうもありがとう」

* * *

「みんな帰っちゃったねぇ〜、あー今日は本当に楽しかったぁ!マスター、今日は本当にどうもありがとうね。こんなに楽しい誕生日は初めてだったよ」
「いえいえ、お嬢様、お喜びいただけまして私めも光栄でございます」
「なにそれ(笑)」
「ははははっ」

「あっそうだマスター、ずっと気になってたんだけどさぁ…さっきのコードのさ… with って何か教えて!?」
「えっ、い、いまから、このタイミングで?(笑)」

(つづく)

---
MIDIファイル入手先:Happy Birthday To You

*1:birthday.mid は事前にDLしておいて下さい。

2011-07-18

Story #9 メソッド?クロージャ?2

「よし、それじゃあ今度はクロージャについてやっていくよ」
「はーい」

クロージャは変数としても扱えるメソッドみたいなものなんだ。この例を見てみて」

def clos = {
  println 'hello'
}
clos() // => hello と表示される

clos って変数に { ... } を代入してる例だよ。{ ... } の部分がクロージャっていうんだ」
「ふーん、じゃあ、その下にある clos() っていうのは?」
「ここは、クロージャに () って付けて呼び出しているところだよ。ちなみに、() は .call() のシンタクスシュガーだよ、だから」

clos.call()

「って書いても同じだよ」
「ふーんそうなんだ。ところでマスター、これってさ、さっきのメソッドとは何が違うの??例えば…」

def clos() {
  println 'hello'
}
clos() // => helloって表示されるでしょ?

「こうやっても結果は全く同じじゃない?」
「するどいね、ここまでの説明だったらクロージャメソッドも違いはないよ。じゃあどこが違うかを教えようか」

def method() {
  println 'hello'
}
method() // <- 実行できる
clos()   // <- 実行できない(宣言前だから)
def clos = {
  println 'hello'
}

「たとえばこんな風にすると、clos() のところでエラーになるよ、なんでだか分かるかい?」
「コメントに書いてあるじゃん?宣言前だから、でしょ?」
「正解!答えを先に書いちゃったけど、“宣言前” の意味は分かるかな?」
clos はここの場合だとまだ def されてないから」
「そうそう、その通り。じゃあ、これを両方とも動くようにしてみようか」

def clos = {
  println 'hello'
}
method() // <- 実行できる
clos()   // <- 実行できる(宣言後だから)
def method() {
  println 'hello'
}

「こんな感じだね」
「あれ?method() の方は宣言前に使ってるけど、大丈夫なの??」
「いいところに気付いたね、それが大丈夫なんだよ」
「どうして?」
「ええとね、実はメソッドっていうのはクラスに付属するものなんだ。ここの場合だと、def method() { ... } って書くとスクリプトっていうクラスのメソッドとして宣言したことになるんだよ」
「うんうん」
「で、クロージャっていうのはどこにも属さない自由な存在なんだ」
「フリーターみたいなものなのね?」
「ははは、まぁ、そんな感じだね。それで、クラスに属しているとそのクラスを使える場所ならどこからでも呼び出せるようになるんだよ。だけど、クロージャは宣言された後からじゃないと使えないってわけさ」
「へぇ、なんとなくわかったけど、それじゃあどこからでも呼び出せる分、メソッド方が便利なんじゃない?
「まぁ、そうだね…ここまでの説明だとそう考えてもしょうがないかもしれない、それじゃあクロージャの方が便利なところを二つほど説明しようか。一つは既に説明した、変数に入れられること、そしてもう一つはこのコードを見てみて」

def name = 'tsuki'
def clos = {
  println "hello $name"
}
clos() // 何て出力される??

「さて、ここでクイズです、このコードは何て出力されるでしょうか?」
「いつからそんなクイズ風味になったの?…まぁいいわ、ええっと…、たぶん、"hello tsuki" って出力される!」
「正解!どうして分かったの?」
「えっ、なんとなく?勘で」
「勘かぁ、まぁでもそれは正しいかもしれない。実はクロージャクロージャが宣言された時に見えていた変数は見えたままなんだ、変数を自分自身(クロージャ自身)に閉じ込めているイメージといってもいい、だからClosure(クロージャ)って名前なんだね」
「へぇ、閉じ込めるからクロージャかぁ、なるほどぉ!ってことはここの場合、nameが閉じ込められてるってわけね」
「そうだね、だから {...} の外側の name も使えるわけだ。もっと詳しくは使っていくうちに分かってくると思うから今はこのくらいの理解でいいかな。ちなみに、メソッドではここであげた二つのことはできないんだ。あと、メソッドクロージャにして取り出すなんてこともできるんだけど、それはまた今度ね」
「はぁーい」

「あとちょっと言いそびれたけど、クロージャにもメソッドと同様に引数を渡せるんだよ。その書き方の違いを表にまとめておこうか」

メソッドクロージャ説明
def method() { ... }def clos = {-> ... }引数なし
def method(a) { ... }def clos = { a -> ... } または def clos = { it }引数一つ
def method(a, b) { ... }def clos = { a, b -> ... }引数二つ

「こんな感じにクロージャだと -> の左側に引数を書けばいいんだよ。->の左側に何も書かなければ明示的に引数がないということを表せるし、引数と -> 自体を省略すれば暗黙引数として it が使えるようになるんだ」
「そっかぁ、it っていうのは引数を省略したときの書き方だったんだね」
「その通り!」

「さぁ、今日も遅くなっちゃったね、続きはまた今度だよ」
「はぁーい、またね!マスター、バイバイッ!」