2024.12.19
システムの穴を運用でカバーしようとしてミス多発… バグが大量発生、決算が合わない状態から業務効率化を実現するまで
ScalaのコンパイラにFizzBuzzを解いてもらう(全1記事)
リンクをコピー
記事をブックマーク
@jooohn1234氏:じゃあ発表させていただきます。「ScalaのコンパイラにFizzBuzzを解いてもらう(Dottyもあるよ)」ということで、ちなみにDottyというのは、現行はScala2がメジャーバージョンなんですけど、Scala3にあたるやつです。Scala3の書き方も紹介しますという発表です。
私はエムスリーの冨岡と言います。
右下に可愛い女の子がいると思うんですけど、これは私の娘をイメージしていて、1歳半のつむぎちゃんという、スライド中にも何回か出てくるので「つむぎちゃんだな」と思って聞いていただければと思います。
(会場笑)
今日はFizzBuzzの話をするんですけど、Wikipediaを見てみると「コードが書けないプログラマ志願者を見分ける手法」と書いてあって、これにはもうつむぎちゃんもビックリで……。
(会場笑)
恐ろしいなと。やばいですね。「ちょっとFizzBuzz書けないとやばいな」となるんですけど、問題はどういう問題かと言うと、1〜nまでの数について以下のように出力しますと。3で割り切れる場合は"Fizz"で、5で割り切れるときは"Buzz"、3でも5でも割り切れる場合は"FizzBuzz"、その他の場合はその数を出力するみたいな感じです。
「もう難しすぎる〜」みたいな感じですよね。
(会場笑)
ちなみにNP困難は関係ないんですけど「まったくわからん」と。
つむぎちゃんも1歳半なのでまったくわからないです。
(会場笑)
ここでマリー・アントワネットという人の言葉を借りると「FizzBuzzを自力で解けないんだったら、コンパイラに解いてもらえばいいんじゃないか」と言っている。
ということで、わからないけどコンパイラに解いてもらえばいいんだということで解いていきます。
順を追っていくと、まずコンパイラに数を認識してもらう、コンパイラに数値演算をしてもらう、そして割り切れる判定をしてもらって条件分岐して繰り返しをしてFizzBuzzを解いてもらおう、という順番でやっていきます。
まず「コンパイラに数を認識してもらおう」です。たぶんこの勉強会に来ている人は「そうか」みたいな感じだと思うんですけど、ペアノ数というやつがあって、0か、ある自然数のその次みたいな感じで自然数を定義できそうですね。自然数に0が含まれるかどうかみたいな話は諸説あるんですけど、含まれるとしてください。
これはみなさんもわかっているとします。これをやると何がうれしいかと言うと、コンパイラが0と1を見分けてくれると。
「= 〇〇」というScalaのやつがあるんですけど、(スライドを指して)これは左と右の型が一致しているかどうかを見ていて、0はSucc[Zero]、つまり1みたいなものですけど、「0は1である」みたいなことをやろうとすると「そうじゃないよ」とScalaのコンパイラに怒られるわけです。
それでコンパイラが数字を認識してくれるようになったと。整数型に変換する方法も別に用意しておきましょう。0から5ぐらいまで用意しておくと便利です。
次は「コンパイラに数値計算をしてもらおう」ということで、ここからはおそらくみなさんScalaをやったことある人だったらわかると思うんですけど、そうじゃない人のために、implicitというScalaの不思議な機能について説明させていただきます。
implicitとは何かというと、implicitly[string]と2行目に書いてあるんですけど、これはこのスコープ内にあるStringのimplicitの値を持ってくるみたいなことをやっています。
上にimplicitのStringとして"I am implicit"と定義されていて「implicitなのはこれだ!」とScalaのコンパイラがいい感じに見つけてきてくれて、それを持ってきてくれるというのがimplicitの基本です。
implicitはけっこういろいろできて、これはちょっと難しくて複雑な例になるんですけど、(スライドを指して)これはimplicitのスコープ内にあるんですけど、スコープの中にまた別のimplicit aがあったらBox[A]もimplicitとして解決できるよ、みたいなことを書けます。
implicitly[Box[String]]みたいなのをやろうとすると、StringがimplicitであったらBoxStringも解決できるという感じなんです。Box(I am implicit.)と。
これは再帰的に解決してくれるので、[Box[Box[Box[String]]]]みたいなのをやるとBox(Box(Box(I am implicit.)))というかたちで再帰的に解決してくれます。これを使います。
それで、「コンパイラに数値計算をしてもらおう」という話なんですけど、Diffというのを考えます。これは何かというと、ちょっともうボイラープレートが多すぎてわけがわかんないんですけど、言いたいのは「何か-0」はそのまま「何か」ですよね。「a-0」は「a」ですよねと。
それと、何かをaだとすると「aの次-bの次」は「aーb」と同じですよね、みたいなことを言っていて、「aーb」がimplicitのスコープ内にあれば「aの次-bの次」も証明できるみたいな感じのことをやっています。
それで、こういうのをやるとScalaコンパイラがいろいろ数値計算をしてくれて、この3-2、Diff[_3,_2]で、「1は3−2である」みたいなことをimplicitに解決してくれというと、怒られません。でも「2が3−2です」みたいなことをやろうとすると怒られる、という感じで引き算ができるようになりました。
これの応用で「割り切れる判定をしてもらおう」という感じなんですけど、同じような感じで0は分母で割り切れるというのと、分子-分母は分母で割り切れるみたいなものがどちらかあれば割り切れるよね、と定義します。
そうすると「4は2で割り切れる」というのは怒られないですが、3は2で割り切れるというのは怒られる。こういうことができます。
次、コンパイラに条件分岐をしてもらおうと。Scalaの特殊な解決の優先順位みたいなのがあります。
implicitの[IsEven[_3]]の値を探したいなとなったときに、コンパニオンオブジェクトと言って、シングルトンオブジェクトというかこの型に関係あるオブジェクトみたいなものがあります。こいつの中にimplicitの定義が含まれているかどうかを見てくれるんですけど、その中でも優先順位があって、先に継承ツリーの近い順に解決してくれるんですね。
なので、これはHighPriorityを継承していて、そいつがLowPriorityを継承してるという感じになっているんですけど、こっち(HighPriority)を先に見つけようとしてくれます。見つからなかったらこっち(LowPriority)みたいな感じになっています。これを使って3と0がIsEvenかというのを判定できるようになっています。
FizzBuzzに当てはめると、3でも5でも割り切れるとか、3で割り切れる、5で割り切れるみたいなものをそれらしい優先順位でトレイトを継承していくと。
そういう感じでやると、FizzBuzzValueの1は1で、2は2で、3はFizzで、5はBuzzで、15はFizzBuzzで、FizzBuzzぽくなってきました。
そして、「コンパイラに繰り返してもらおう」というのが次です。これは、結局再帰もできるので繰り返しできるという話ですね。
すると、こんな感じになると。
もうみなさん天才なのでわかるかと思います(笑)。
(会場笑)
「コンパイラにFizzBuzzを解いてもらおう」ということで、これはプリントをするというのがうまく表現できなくて、もっと良い方法があったら教えてくださいという感じなんですけど。
このPrintFizzBuzzMessageというものの値が解決されたときに、その値を素直にPrintlnするというのを用意しておくということです。
それで、そいつを使って[Range[PrintFizzBuzzMessage, _1, _15]]みたいな感じで実行するとこういうふうになるやつがコンパイルされると。
「やったね」と。「ifとかforとかまったくわからないけどFizzBuzzができました!」となるんですけど、こんなややこしいのやりたくないですよね。
なので、Dotty、Scala3だとどれだけ簡単になるのかというのをここからやっていきます。
コンパイラに数を認識してもらおうという感じなんですけど、Dottyはliteral typeというものがあって、1とかを素直に認識してくれます。
しかも、Scala2.13にもliteral typeがあるんですけど、その次みたいなものはないんですね。これはDottyだとあります。
最初からSという……Sでいいのかという感じがするんですけど、Sというのがあって、下を見てみると、2はS[1]であるとやると怒られないですけど、2はS[3]であるとやると怒られる。しかも怒られ方も「Int(2)はInt(3)じゃない」みたいにいい感じにやってくれます。Scala3はすごい。
型から値を導出したいというときも、Scala2だとほとんどの場合で型クラスみたいな仕組みを使わないといけないと思うんですけど、constValueというのがあって、ほぼ型クラスに近いんですけど、そのままリテラルが返ってくる、みたいなことができます。
数値計算をするのはめちゃめちゃ簡単です。
Scalaはmatch typeという邪悪な機能があって、型をmatch式に当てはめられるみたいな感じで、「AからBを引きたい」と書こうとすると「Bが0だったらAですね」と。AがSuccの何かで中身もSuccの何かだったら、中身同士をマイナスするみたいなほぼ普通のプログラムじゃないかみたいなことが書けます。いい感じで動きますね。
ちなみに、これはでかい計算はできなくて、10,000-10,000みたいなことをしようとすると怒られます。
「コンパイラに割り切れる判定をしてもらおう」も、match typeできます。
なんでもできるので、なんでもできると。
(会場笑)
条件分岐もmatch typeでできます。
ちなみにこのへんはいい感じにできたなと思うんですけど、"Fizz" | "Buzz" | "FizzBuzz" | Intと書いて出てくる値を全部リテラル型に投れる何かに……しかもunionですね。Scala3でやっと出てきたunionというのがあって、こういうのを使うとかなりいい感じに書けます。
3で割った余りと5で割った余りが(両方とも)0だったら"FizzBuzz"と表示するという、そのまま普通のプログラムみたいに書けます。確かにそれらしく動きます。
「コンパイラに繰り返してもらおう」というのも、結局1の次が2でその次がFizzみたいにいろんな型がごちゃ混ぜになったやつをやるんですけど、これはいわゆるHList(Heterogeneous List)というのがあるんですけど、Scala3では標準で用意してくれていていい感じにやれます。
しかもTuple.Mapみたいな、それぞれのHListになんとかマップしたいやつみたいなものも書けます。すごい。
最終的にどうするかというところでPrintするところなんですけど、Scala3ではinlineというのがあって、コンパイル時にinline化をしてくれるやつですね。
それを使うといい感じに書けて、これはコンパイル結果でコンパイルしたやつです。
ちょっとノイズがあってPrintln1、2、Fizz、4、Buzzとなって、FizzBuzzの答えが上から順番に実行されるみたいなコードが生成されます。これはすごくないですか?
(会場笑)
まとめとしてScala2の場合、implicitの機能を使ってコンパイラにFizzBuzzを解いてもらったんですけど、なんかすごい難しくややこしい方法でできました。「コードが書けないプログラマ志願者」と言われずに済みそうですね。
ちょっとコンパイラの力を借りてそういうことができる。Dottyに関しては難しいことを考えなくてもできるので、簡単すぎておもしろくないという欠点があります。つぐみちゃんもちゃんとプログラマとして認められた。
改めてこれを言うと、もしみなさんの中でFizzBuzzが書けない「ちょっと難しすぎるなぁ」という人がいたら、コンパイラに解いてもらえばいいんじゃないかという発表でした。
ありがとうございます。
(会場拍手)
関連タグ:
2024.12.20
日本の約10倍がん患者が殺到し、病院はキャパオーバー ジャパンハートが描く医療の未来と、カンボジアに新病院を作る理由
2024.12.19
12万通りの「資格の組み合わせ」の中で厳選された60の項目 532の資格を持つ林雄次氏の新刊『資格のかけ算』の見所
2024.12.16
32歳で成績最下位から1年でトップ営業になれた理由 売るテクニックよりも大事な「あり方」
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
PR | 2024.12.20
モンスター化したExcelが、ある日突然崩壊 昭和のガス工事会社を生まれ変わらせた、起死回生のノーコード活用術
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.18
「社長以外みんな儲かる給与設計」にした理由 経営者たちが語る、優秀な人材集め・会社を発展させるためのヒント
2024.12.17
面接で「後輩を指導できなさそう」と思われる人の伝え方 歳を重ねるほど重視される経験の「ノウハウ化」
2024.12.13
ファシリテーターは「しゃべらないほうがいい」理由 入山章栄氏が語る、心理的安全性の高い場を作るポイント
2024.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
Climbers Startup JAPAN EXPO 2024 - 秋 -
2024.11.20 - 2024.11.21
『主体的なキャリア形成』を考える~資格のかけ算について〜
2024.12.07 - 2024.12.07
Startup CTO of the year 2024
2024.11.19 - 2024.11.19
社員の力を引き出す経営戦略〜ひとり一人が自ら成長する組織づくり〜
2024.11.20 - 2024.11.20
「確率思考」で未来を見通す 事業を成功に導く意思決定 ~エビデンス・ベースド・マーケティング思考の調査分析で事業に有効な予測手法とは~
2024.11.05 - 2024.11.05