Chatwork Creator's Note

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

読者になる

あなたの知らないScalaの世界~Scalaでコードゴルフ~

挨拶

初めまして、2020年6月に大学院を中退し*1新卒として入社した田尻(@lmdexpr)です。

人生は色々ですね。

今はサーバーサイド開発部(Scala)で元気に毎日Scalaを書いています。

その辺の経緯にも軽く*2触れているインタビュー記事はこちら ↓

compass.labbase.jp

さて、今回は「あなたの知らないScalaの世界」と銘打ちまして、Chatwork Advent Calendar 2020 / Scala Advent Calendar 2020 四日目の記事としたいと思います。

全く堅くない、業務とは無関係どころかしばらくチームメンバーに口を聞いてもらえないかもしれないような*3テクニックのお話です。

コードゴルフとは

今回お話しするのは「コードゴルフ」についてです。

「コードゴルフ」とは、ソースコードを極力短くして何らかの問題を解決するという競技のことを指します。

とは言っても僕は特別なプレイヤーという訳でもなく暇な時に考えたりする程度の言わばエンジョイ勢です。

コードゴルフに選定される言語は多種多様ですが大体の場合は「型が弱く」「何に使うのか分からないような謎の機能があったり」「非安全な仕様がある」言語が多い気がします。*4

何とは言いませんが。

そこで僕がお話しするのは真逆の言語「Scala」です。*5

そして肝心の題材として、今年、オンラインで開催された「ScalaMatsuri2020」の二日目、アンカンファレンスの一枠として設けられていた「Scala酷いコード選手権」に30分くらいで書き殴って提出した物を取り上げたいと思います。

「どこから見てもJsonパーサーですね」

ScalaMatsuri2020 Scalaひどいコード選手権用 JSON parser

こちらが当日提出したコード……にはバグがあったのでそれを修正する形で書き換えたものです。 *6

あなたの知らない(知らなくても良い?)Scalaの世界が見えてきたでしょうか?

今回のコードは合計730文字となりました。

肝心の動作ですが、

{"outer": [{"inner": [1,2,3], "aaa": null}, false, -1, 1.0]}

のようなJsonを流してやると、

Map(outer -> Vector(Map(inner -> Vector(1, 2, 3), aaa -> null), false, -1, 1.0))

のような結果が返ってきます。

例外処理や文字列中のエスケープ文字などやっていないことも多いですが、まぁ、十分でしょう。

次は細かいテクニックの説明をしていきます。

良い言語ではゴルフがし辛い

Scala は良い言語です。

何故ならゴルフがし辛いからです。

例えば強い静的型付けですから適当なコードを書こうとしてもうまくいきません。

普通であれば良いことなんですが今回に限ってはうざったくてしょうがないです。

という訳で、

テクニックその1「Anyで誤魔化す」

Jsonという構造はどんなデータが返ってくるか分かりません。

整数だったり、オブジェクトだったり。

しょうがないので Any で誤魔化します。

ゴルフ的には _ を使いたいんですが、こちらだと推論されてしまって Int にしたいのに Double になってしまったりします。

使えるところは _ を使っていきますが型を厳密にして欲しい時は(むしろ厳密にして欲しくない時は?)Any で積極的に誤魔化しましょう。

テクニックその2「contains は8文字もあるが toSet なら5文字で済む」

タイトル通りです。

Set.applySeq を作って contains するよりもコード長が短い*7です。

他にも「toString は8文字もあるが +"" なら3文字で済む」とか「Seq.empty は9文字もあるが Nil なら3文字で済むし、""に至っては2文字で済む」とかもあります。

テクニックその3「? は変数名です」

はい、Scalaでは記号 ? などを変数名に使ったり出来ます。

「え?でも文字数気にするにしても1文字変数で良くない?」と思われそうですがゴルフ的には大変重要な意味があります。

例えば

val a="abc"
a.toSeq

と書きたい時です。この時に ? を使うと、

val? ="abc"
?toSeq

という具合に何と . を1文字も削減できるのです!

実際には ? toSeq と解釈され、Postfix Operator という扱いになっている訳ですが、通常のアルファベットではどこが区切りなのかコンパイラに教えてやることができないため空白を入れる必要があります。

ただしコレは諸刃の剣でして case hoge =>? というような記述はできません。

この場合は case hoge => ? と書く必要があります。空白分の1文字差があることにお気付きでしょうか?

どうやらScalaコンパイラは記号は記号、文字は文字という具合に区切って認識するらしく、記号を使う場合は他の記号と引っ付ける事はできないようなのです。*8

よって気を付けて使わなければ逆に文字数を増やすことにもなりかねないので注意が必要なテクニックです。

テクニックその4「アルゴリズムを見直そう」

もはやテクニックでもなければ Scala 固有でもないのでさらっと言いますが、特に Scala くらい「しっかりした」言語だと変なテクニックを探すよりもアルゴリズムの改善が一番短くなる方法な気がします。

コードゴルフは業務とも競技プログラミングとも違うベクトルの競技なので安全性とか速度とかを放り投げていかに短くするか、という度胸が必要です。

その他のテクニック

特に僕が面白いかなーと思ったものを紹介しましたが、テクニックを知りたければ参考サイトとして https://codegolf.stackexchange.com/questions/3885/tips-for-golfing-in-scala を紹介しておきます。

業務では一切役に立たない知見が色々と書かれています。

まとめ

Scala でコードゴルフはあんまりやらない方がいいので perl とか書こう。

今回のオチ

scala.util.parsing.combinator を使えば↓のように390文字でバグのないJsonパーサー(正確にはvalidator)が書けるみたいです。

codegolf.stackexchange.com

ライブラリってすげー(大敗北)*9

宣伝

Chatworkで僕とコードゴルフバトルしませんか

hrmos.co

hrmos.co

*1:不思議な歪みで実際に中退したのは9月の末日

*2:本当に軽く

*3:業務でそんなコードを書いていたらクビになってもおかしくないでしょうからこんなことを言っている場合ではありません

*4:個人の感想です

*5:謎の機能はあるかも知れません

*6:実は Revisions を見ると分かりますが当日書いたものからはかなり形が変わっています

*7:速くもある

*8:要出典

*9:ライブラリ自体のコード入れればこっちが圧勝ですけどね!!!