Gitをバックエンドにしたタスク管理bot


この記事は、ドワンゴ Advent Calendar 2017 の4日目の記事です。

TL; DR

すごい簡単なゆるいタスク管理のバックエンドに、内容アドレスファイルシステムとしてのGit使うのもまあいいんじゃないの? とおもってGemを作った。

ゆるいタスク管理システムが必要だった

通常、仕事のタスク管理はJIRAとかRedmineとかGithubとかTorelloとかなんかそういう専用のやつを使うと思います。とはいえ、「もう今日は帰ってるけど、明日こののプルリク見てください」とかSlackで伝えたり、「このプルリクレビュー通ってるんで、明日マージしといて」とかSlackで伝えたり、その程度のことをチケットにするのも妙な感じです。デイリーミーティングや口頭やSlackで伝えれば良い気もしますが、まあそういうのって大抵忘れます。そもそも言っても忘れ去られるし。Slackのリマインダも使いづらい。そういう、なんか忘れるけど伝えときたいことを忘れないようにしたいなと思ったら、とりあえずbotを作ります。

botを作ることの理由を問われても、まあなんとなくとしか言いようが無いですが、Slack上のbotだったらまあ大体みんな見てるだろうという程度の理由です。

簡単なタスク管理システムってなんだろう

頭にぽわっと思いついた程度の要件です

  1. FIFO型のキュー
  2. キューの操作履歴の参照
  3. Slack上からキューを操作可能

普通の配列操作とSlack botじゃんと思いましたが、肝心なのはキューの操作履歴の参照です。

キューの操作履歴、例えば新しいタスクをキューに追加したとか、先頭のタスクを完了したとか、n番目とm番目のタスクを入れ替えたとか、そういう類の操作履歴を残そうと思うと、これはなかなか厄介な気がしました。なにせ、方法がパッと思いつくだけで5個くらいあり、そのどれもが概ね「DBかファイルに操作履歴を残す」という方法です。ただ、DBを使うのは大掛かりで嫌だし、ファイルに書き出すのはロックの問題や破損の問題に立ち向かうのが億劫です。

もう少し何か手軽でいい感じの無いかと考えたところ、GitのようなVCSをキューのバックエンドに使えば、操作の履歴を完全に残して参照も手軽なタスクシステムが作れるのではないかと思いました。

libgit2を使おう

キューのバックエンドにGitを使うアイディアを出したは良いものの、普通にSlack botからGitの操作をすると、次のような点で困ります。

  1. Botを動かす場所にGitコマンドが必要
  2. GitはChatBotのような並列操作で同時に扱うとワークスペースを壊す
  3. 遅い

これらの問題に立ち向かう方法は、libgit2を使うことです。

https://libgit2.github.com/

libgit2は、Cで書かれた組み込み用のGitライブラリで、たくさんの言語へのバインディングとともに配布され、Gitコマンドに依存せずにGitの操作が可能です。また、通常のGitコマンドと違い、Gitの内部コマンドを直接呼び出すため、動作も高速です。それだけでなく、通常開発者が使うGitのコマンドの裏に隠されたアトミックな操作を直接行うため、完全とは言いませんが、ある程度の並列実行にも耐えられる安全性を持っています(ワークスペースが壊れて、Git操作を受け付けなくなるとかが起こらなくなる)。

libgit2を使うことで、Botを動かす場所にGitコマンドが必要なく、並列操作にある程度耐え、本来のGitよりも早く処理を実行することが出来ます。

しかしその一方で、大きな欠点もあります。それは、libgit2で扱えるGitの世界は、我々が普段使っているGitコマンドとは大きく違う点です。libgit2を使ってGitリポジトリを操作するには、非常に複雑で手間のかかる手順と、Gitの裏側の世界への理解が必要です。

本当のGitの世界

最初に書いておきますが、この段落の内容に関してより詳しく理解したい場合は、Pro Gitの10章を読んで下さい。

Gitの世界には、通常開発者が利用する add や checkout や push などの磁器(Porcelain)と呼ばれるコマンドの裏で、隠された配管(Plumbing)と呼ばれるコマンドが複雑に呼び出され、操作されています。

簡単に、Gitには表側の世界と裏側の世界があると考えてください。表側の世界は、我々開発者が普段見ている、VCSとしてのGitです。ファイルに変更を加え、ステージし、コミットして変更履歴を記録する。それが、裏側の世界のGitです。一方で、裏側の世界のGitは、内容アドレスファイルシステムです。内容アドレスファイルシステムは、少なくともGitの世界観ではほぼほぼKVSシステムとほぼ同じようなものと理解して問題ないです。ざっくり言うとGitはKVSです

表側の世界を操作するのが、先にも出てきた普段みなさんが慣れ親しんでいるGitコマンドです。そして、裏側の世界を操作するのが、今回つかったlibgit2です。もちろん我々のよく知るGitコマンドでも、裏の世界を操作することは出来ますし、そのためのコマンドが(普段は使わないけど)用意されています。しかし、libgit2は完全に裏の世界のために存在するライブラリで、表の世界のような使用方法は出来ません。

内容アドレスファイルシステムとしてのGit

例えば、Gitのワークスペースにあるファイル「something.txt」を追加して、内容を編集しコミットすることを考えてください。通常の我々の操作では、次のようなことを行います。

ファイルを編集し、git add コマンドでファイルをステージし、git commit コマンドで新しいコミットを作成します。それでは、その時にGitの裏側の世界では何が起こっているかを見てみましょう。

git add

あるファイルに対して add を行った時、Gitの裏側の世界では次のようなことが起こります。

  1. add されたファイルの中身と、ファイルのメタ情報のSHA1ハッシュ値を計算する
  2. ファイルのメタ情報と中身をNULL文字で連結し、zlibでその情報を圧縮する
  3. 圧縮した内容を、1で計算したSHA1の値のファイル名に書き出す
    1. 正確には、SHA1値の先頭2文字のディレクトリ下に、末尾38文字のファイルを作り、そこに書き出す
    2. ex. ハッシュ値が d670460b4b4aece5915caf5c68d12f560a9fe3e4 であれば、.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 というファイルに内容を書き出す
  4. ワークスペースのファイルとディレクトリの構成をもとに、ツリーオブジェクトを更新する

ここで重要なのは、Gitではあるファイルのある時点の状態に、そのファイルのSHA1値でアクセスできるKVSだということです

Gitの裏側の世界では、ありとあらゆるファイルとディレクトリは、オブジェクトとして扱われます。上の例では、addされたファイルはblobと呼ばれるオブジェクトです。オブジェクトには、その内容と属性で一意のSHA1値が振られていて、Gitの裏側の世界ではこのSHA1値を使うことによってファイルにアクセスすることが可能です。

blobオブジェクトは、あるファイルのある時点での完全な内容をzlibで圧縮したもので、Gitはその内容にSHA1の値のキーでアクセスできる状態です。これが、Gitが内容アドレスファイルシステムと呼ばれる理由です。ファイルのSHA1値というキーを知っていれば、そのファイルの内容にアクセスすることができます。それでは、今現在のワークスペースの内容を、SHA1値のキーで表現するには、どうすればいいでしょうか? その仕組が、ツリーオブジェクトです。

ツリーオブジェクトとは、Linuxのディレクトリ構成に似た情報が書かれたファイルです。参照ツリーの内容の例として、次のようなものが挙げられます。

各行の最初のブロックがファイルのアクセス権限、次のブロックがオブジェクトのタイプ、その次のブロックがアクセスすべきオブジェクトのSHA1値で、最後のブロックがファイル名です。これは、Linuxのシェルで ls -la コマンドを打った時の情報によく似ています。blobをファイル、treeをディレクトリと置き換えれば、ほぼおなじ情報が得られます。これらの各行がファイルの情報と参照先を表しています。

参照ツリーは入れ子構造にすることが出来ます。つまり、ルートの参照ツリーの下に、サブディレクトリとしての参照ツリーを入れることも可能です。これによって、ファイルのディレクトリ構成を表現しています。

このように、Gitの裏側の世界では、すべてのファイルとディレクトリにはSHA1値が割り振られ、その値を入れることでファイルとディレクトリの内容を参照することができます。

git commit

add コマンドでステージングしたファイルを変更履歴としてコミットするには、 git commit コマンドを使います。コミットに関しては、Gitの表の世界のコマンドと大きな違いはありません。Gitは次の情報を集め、コミットオブジェクトを作ります。

  • Author
  • Committer
  • Message
  • ルートのツリーオブジェクト
  • Parent(一番最初のコミットのときは、この値は含まれない)

AuthorやCommitterやMessageは、通常の git commit コマンドでもよく見るので、何かわかると思います。Parentは新しくつるコミットの前のコミットで、git log コマンドで見ることができるコミットの並びで親に該当するものです。

目新しい情報は、ルートのツリーオブジェクトです。これは、git add コマンドの裏側で作られた、ツリーオブジェクトです。ステージングで作られたツリーオブジェクトは、現在のステージングの内容のすべての内容にアクセスできるSHA1値が記録されていて、Linuxのディレクトリのようにたどることが出来ます。ステージングの際に作成されたツリーオブジェクトの情報をコミットに入れることで、ステージングの内容を、コミットとして確定させることができるのです。

Gitのコミットとは、ある地点のツリーオブジェクトのSHA1値に対して親のコミットオブジェクトのSHA1値とコミッターの情報を与えたものです。そしてコミットオブジェクトも当然オブジェクトであり、SHA1値でアクセスが可能です。Gitの表の世界でも、コミットのSHA1値はよく目にすると思います。あの値は、Gitの内容アドレスファイルシステムの世界では、コミットオブジェクトを参照するためのキーであり、ファイルやツリーのオブジェクトを参照することとほぼ同じなのです。

改めて、libgit2を使おう

Gitの裏側の世界を理解すれば、libgit2を使ってgitリポジトリの操作が可能です。それでは、libgit2のRubyバインディングであるRuggedを使って、実際にリポジトリを操作してみましょう。

Ruggedを使ってgitリポジトリするには、次のようにします。

これで、リポジトリオブジェクトが作成できます。先程まで説明していたGitの内容アドレスファイルシステムの挙動を実際に見てみましょう。

writeメソッドでリポジトリにblobオブジェクトを書き込み、exists?メソッドでGitのオブジェクトの中に作成したオブジェクトが存在するか確認しています。そしてその後、readメソッドでオブジェクトを読み込み、そのオブジェクトのdataメソッドでblobの中身を読み取りました。

このように、Gitリポジトリをlibgit2で操作すると、まるでKVSのようにblobファイルを保存できるデータベースとして扱えます。同じように、コミットも見てみましょう。

先ほど作成したblobをステージング(index)の参照ツリーに追加し、その状態をtreeオブジェクトとしてGitのに記録します。そして、その時に発行されたSHA1を利用して、コミットオブジェクトを作成しました。当然、コミットもオブジェクトで、SHA1のオブジェクトIDが発行されます。

この後も同じように、新しいblobオブジェクトを作成し、ステージングしてtreeオブジェクトを作成し、そのtreeと先に作成したコミットのオブジェクトIDを元に次のcommitオブジェクトを作成します。これが、Gitで行われているバージョン管理の最も根本的な部分です。

libgit2を使って、Gitをバックエンドにしたキューを作成する

全体のコードは長いので、GitQueueというGemを作りました。次のコードは、簡単な使い方のスニペットです。

出力は次の通り

Arrayのようなインターフェイスを持っていますが、Arrayとは違いFIFOの処理を邪魔するようなメソッドは生えていません。例外的にタスクの順序入れ替えは出来ますが、末尾挿入と先頭取り出し以外の方法でタスクを出し入れすることは出来ません。

また、今回の実装では入っていませんが、Ruggedではlibgit2のバックエンドに、OSのファイルシステム以外にもRedisなどのKVSを選択することも可能です。普通にGitのバックエンドをKVSにすると書くとよく意味がわかりませんが、Gitという内容アドレスファイルシステムのバックエンドを本物のKVSにすると考えると、すんなりわかりやすいと思います。libgit2のバックエンドを本物のKVSにすることによって、より堅牢で様々な場所からアクセスできるGitQueueの運用などの夢も膨らみそうです。

Slack bot を作る

このエントリの目的はGitをバックエンドとした履歴記憶機能付きキューでタスク管理botを作ることなので、スニペット程度ですが簡単にbotを実装してみます。

色々と処理が雑ですが、スニペットなので許してください。

このbotの機能としては

  • Adminユーザーが、タスク管理を行うユーザーを追加できる
  • 各ユーザーごとに、GitQueueを使ってタスクのキューを作成する
  • ユーザーは、タスクの追加とタスクの完了ができる
  • ユーザーは、タスクの一覧を見ることができる

です。

肝心の操作履歴をどこに使ってんだって気はしますが、概ね操作履歴なんていうのは何かしら問題が合った時の調査に使うものなので、別にbotからは見えなくていいと思います。実際にリポジトリにアクセスすれば、使い慣れたGitコマンドでキューの変更履歴を参照することができます。

まとめ

  • Gitの実態は内容アドレスファイルシステム、つまりほぼKVS
  • GitをFIFOのバックエンドに使うと、操作履歴が全て残って素敵
  • libgit2を使うことによって、少ない依存で強力なGit操作が可能
  • botを作るのは楽しい

カテゴリー: Ruby, Slack, 未分類 | コメント / トラックバック: 0個

apt-get upgrade したら、Chromeが物故割れたのでなおした


Chromeがぶっ壊れる

UbuntuのChromeをどういうふうに管理していたのか自分でも忘れていたが、今までは普通にChrome自身のアップデートで、Chromeを再起動するたびに勝手に最新になっていた。しかし、ちょっと必要なパッケージがあり、何も考えず Ubuntu 15.04 vivid 上で apt-get update && apt-get upgrade したら、Chromeが起動しなくなった。

何度起動してもクラッシュするので、シェルから起動してみたところ、次のようなエラーが出て起動しないことが分かった。

libnss3のバージョンの問題だったので、アップデートをすれば治るかと思ったが、 15.04 ではChromeが必要とするバージョンを入れることができないことが分かった。

https://launchpad.net/ubuntu/vivid/i386/libnss3/

そのため、わけあって 15.04 を使い続けていたが、最新のLTSである 16.04 xenial に入れ替えることにした。

Ubuntuのメジャーバージョンアップは今までやったことがなかったが、  do-release-upgrade というコマンドを使えば、特に難しいこと無くできるらしい。ただし、それは当然ながらサポート期間内のバージョンの話に限る。今回困った15.04は、はるか昔にサポート期限切れになっており、一筋縄では行かなかったので、ブログに書いて忘れないようにしておく。

do-release-grade が動かない理由と対策

15.04 で do-release-upgrade を実行すると、次のようなエラーが出る。

15.04 (vivid) から、 16.04 (xenial) へのアップデートはできないと言われる。んなアホな。

とても困ったが、ググった結果ここのページに答えのほぼ全てと対策法が書いてあった。

Ubuntu 最新バージョンへのアップグレード

要するに、 vivid と xenial の間には、 wily というもうひとつのバージョンが存在するが、既に wily 自体が Out of date のため、 vivid から wily のアップデートが不可であり、 vivid から xenial への一つ飛ばしのアップデートもできない、というのが理由だ。対策としては、 changelogs.ubuntu.com/meta-release のレスポンスを何らかの方法で乗っ取り(Charlesを使っても良いし、  ダミーサーバーを立てたうえで /etc/update-manager/meta-release の中身を書き換えてもいい)、wilyが有効バージョンであると認識させる。詳しい方法は、リンク先に書いてあるのでそちらを参照。

概ね、この方法ですべてが解決するのだが、最大の問題は wily は既に archive.ubuntu.com からも消えており、上記のサイトの方法ではアップデートができないことだ。

なので、 meta-release の内容を乗っ取るだけでは do-release-upgrade で解決ができない。そのため、 wily のイメージをまだ置いているミラーをミラー一覧から探したうえで、 meta-release の参照先をそっちに向けて、更に /etc/apt/source.list の向け先もそっちにすることで、 do-release-upgrade を利用することができるようになる。

Official Archive Mirrors for Ubuntu

ほとんどのミラーは、最新の archive.ubuntu.com と同期しているため、 wily のイメージが存在しない。だが、 dely しているミラーを参照することで、 wily のイメージが残ったままのものを見つけることができる。

いろいろと見て回った結果、 Psychz Network のミラーに wily の完全なイメージが残っているのを見つけたので、 meta-release と source.list を次のように書き換えた。

secure関連のところはよくわからんかったので何も書き換えていないが、この状態で vivid から sudo do-release-upgrade を実行して wily に更新し、再起動後にもう一度 sudo do-release-upgrade することで xenial に更新することができた。

Chromeの復活

xenial への更新後、  sudo apt-get install –reinstall libnss3 を実行することで、Chromeは復活した。

正直、Chromeが起動しないくらいだったらいっぺんOS消して再インストールしても良かったんだけど、Chromeの中に入ってるクッキーを様々な理由で取り出す必要があったので、こんだけ必死になって修復した。

そもそもサポート切れのバージョンのOSつかうなって話ではあるけど、まあ何か参考になればと思って書き残しました。


カテゴリー: Linux | コメント / トラックバック: 0個

会社で闇LT大会を開催しました


数週間前の話ですが、勤めている会社で闇LT大会を主催し、なかなかの好評を得たのでみんなもやってみてくださいという情報共有です。

TL;DR 普段とちょっと違うLT大会をやることで、社内の交流の促進に繋がりますよ、という話です。

闇LT大会

闇LT大会の開催要項は、次のとおりです。

ルール1:エンジニアリングの話題を話してはならない
ルール2:エンジニアリングの話題を話してはならない
ルール3:発表は1人10分/質問時間なし

大会のタイトルは「闇LT」ですが、なにもアングラな闇の話や、人生や仕事の闇の話をしてほしいわけではなく、単に普段の会社やITイベントでやるLTとは違い、エンジニアリングではない自分の趣味の話をしてほしいというつもりで開催しました。また、エンジニアリングの話題はしないので、普段よりも話すことが多くなるだろうと思って、1人10分の時間を設定しています。

自分はドワンゴという会社に勤めています。ドワンゴでは週に一度昼にエンジニアLT大会を開催しており、そこでは事前に全エンジニアに発表順を割り当て、その週に発表順を迎える人が、業務上の知識共有や趣味のエンジニアリングのLTを行っており、大いに盛り上がっています。

ドワンゴのエンジニアはどなたもエンジニアリング能力が高くて、技術の話題には事欠かないのですが、しかしそれと同じくらい技術とは関係ない趣味を持っている人も多く居ることを知っていました。

当然のことながら、週次で行っているLT大会は、業務時間に行っているという性質上ルールとしてエンジニアリングの話題という制約があるため、面白い趣味の話は技術ネタ以外にはあまり出てきません。そのため、それではもったいないと思い、個人的に話を聞きたいと思っていた趣味を持っている人たちに事前に声をかけて、闇LT大会をやりませんかと登壇をお願いし、エンジニアリングの話題を禁止した闇LT大会を開催しようと思いました。

どんな大会だったのか

闇LT大会は、前半パートと後半パートに別れて開催されました。

前半パート

前半パートでは、主催の自分が事前に声をかけた10名と、自分を合わせた11名が10分ずつの持ち時間を使って、中間の10分休み以外は止まること無くLTをしました。

個人でドラを持っている方が居たのでドラをお借りし、10分が経つと強制的にドラを叩いて次の人に交代する方式です。ちなみに、11人中時間をオーバーした人はほとんど居なかったので、むしろ余った時間を質疑タイムに使ったりしていました。

LTの内容をいくつか抜粋すると

  • 特殊な自転車の話
  • 肉の低温調理の話
  • 同人イベントの開き方の話
  • 海外のカジノで戦った話

など、刺激的なトークが繰り広げられました。

事前に私が話を聞きたいと思っていた方々に声をかけていたため、準備期間があったためスライドなども万全で、どのセッションもとても魅力的な話に満ちあふれて、素敵なLT大会でした。

後半パート

前半パートが終了した時点で時間は22時半頃で、一旦LT大会としては全セッションの終了として締めの挨拶をしました。

そしてその後、1人5分の自由飛び入り枠を開催しました。

飛び入りは、事前に飛び入りを宣言していた人、当日に前半のLTを見て自分も話したくなった人、前半のLTではしゃべり足りなかった登壇者など、どんな飛び入りもすべて受け入れ、かつ飛び入りの希望が無くなるまで(当日は、納得するまで、という表現を使いました)続けました。

その結果、のべ32人の飛び入りがあり、LT大会は午前の2時半まで続けられることとなりました。

飛び入りのLTでは

  • 水槽の話
  • 狐の話
  • アイマスイベントに花を贈る話
  • 廃空港の話

などなど、皆が技術の話題とは関係ない、自分の好きな趣味の話を好き勝手話していて、とてもイキイキとしたLT大会になりました。

食事の話

闇LT大会は、ドワンゴでいうところの夕方くらいの時間から開始され、かつ3時間近くの長丁場なので、食事を用意して参加費という形で徴収しました。予め声をかけた10人の登壇者からはお金を取らず、LTを聴きに来た人たちにご飯代を出してもらう感じの運用です。

最近はコストコにハマっているので、早めに仕事を切り上げ、コストコで60人分くらいのピザや寿司やチキンやサラダや飲み物を書い、車に積み込み会社のセミナールームに運び込みました。予め会場の準備をお願いしていた人たちに協力してもらい、買い出しに行ってる間のセミナールームの机の並べ替えや、食事の運びこみを手伝ってもらいました。

個人の趣味でコストコのご飯を会社の人に布教したかったためにコストコに行きましたが、これは本当に個人の趣味なので普通に出前で良いと思います。飲み物なんかは、バケツに水を張って氷を詰め込み、そこに缶や瓶を入れるとすぐに冷えるし祭りの雰囲気もそこそこあって便利です。

主催の話

基本的に他力本願です。主催者のやることは

  • 事前に社内で面白い趣味を持っている人に声をかけて、登壇を依頼する
  • 会社のセミナールームの予定を抑える
  • イベントやるから、買い出しとか会場の準備とか手伝って、と近くの席の人にお願いする
  • Slackとかの人が多いチャネルで告知する
  • 当日司会をする

これくらいです。

たったこれくらいで、社内の面白い趣味を持った人の話がたくさん聞けて、それに触発された人たちの飛び入りLTまで聞けて、とても楽しい会が開けます。

難しいのは、社内の面白い趣味を持った人を探し出すことかもしれませんが、なんなら社内の名物エンジニアに片っ端から声をかけてみればいいと思います。社内の名物エンジニアのLTというのは、技術の話題は関係なくてもみんな聞いてみたいものだと思います。

予算

これは非常に難しい問題ですが、今回は

登壇者を除く参加者の数 x 0.8(参加表明だけして来ない人の分) x 参加費

で予算を算出して、その中で可能な限り人のお腹を満たす物を買い揃えます。

コストコの巨大ピザは、ひと切れ食べると割とお腹いっぱいになるくらいでかいので、人数分のひと切れ分のピザと、コストコの安い寿司で足元を固めました。

他には、お酒を飲めない人も多いので、アルコールとは別に大量のソフトドリンク類も買いました。これもコストコではアメリカンなサイズでソフトドリンクを売っているので、功を奏しました。

楽しいと思うのでやってみてください

闇LT大会は、毎月やったりするたぐいのイベントではなく、1年か半年に1度、お祭り的なノリで開催するイベントだと思います。

登壇者の方は、自分の登壇が終わるとLTした内容に関して聞いていた人と立ち話をしたりして、交流が生まれていました。

普段は技術の話をしている人たちの、新鮮な一面を垣間見ることができ、そして自分が知らない世界の話をしてくれるので、好奇心がくすぐられる会になると思います。

また、普段あまり社内で話をしない人たちと趣味での交流のきっかけを作ることができると思うので、みなさんもいかがでしょうか。

追記

登壇したことをブログに書いてくれたので、追記しておきます


カテゴリー: ポエム | コメント / トラックバック: 0個

ガールズ&パンツァー シネマティックコンサートを観てきた


ガルパンのシネマティックコンサート、パシフィコ横浜の回を観て(聴いて?)きました。

とても素晴らしかったので、ブログに書いて忘れないようにしておきます。

劇伴がすべて東京フィルハーモニー交響楽団

ガルパンの映画はもう大体全部覚えるくらい観ていますが、劇伴がすべてオーケストラの生演奏で、今まで観てきたどのガルパンとも違った新しい体験でした。素晴らしい。完璧なテイクを録音したパッケージ版の音もそれはそれで良いけど、ライブで楽器の息遣いが感じられる生演奏による劇伴は、爆音上映やBDとは違った迫力があって、興奮しました。オーケストラの方々の真剣に演奏する緊張感と、指揮の栗田さんのバッキバキな動きが小気味良くて、わくわくです。

シネマティックコンサートではオーケストラの演奏が最重点なので、戦車の駆動音や爆発の音はだいぶ弱めに調整されているのが少し物足りないといえば物足りないけど、メインはオーケストラの演奏を楽しむことだからそれでいいのです。

上映の構成は、大学選抜との試合の直前に各校が参戦してくるまでが前半、20分の休憩を挟んで作戦会議から後半。休憩に入る前のオーケストラ演奏もあって、すごい。

カンテレまで生演奏

劇中でミカがなんかしゃべるたびにポロロンするカンテレも、大洗でよくカンテレを演奏しているあらひろこさんによる生演奏。ミカがなにか言うたびに、舞台上でカンテレを弾いていました。当然、継続高校が大活躍するカール自走砲のシーンでも、ずっと生演奏。最高。

当然エンディングも生歌

EDのPieces of youthも、オーケストラアレンジでchochoさんによる生歌。最高すぎる。大宮公演のときは緊急入院で参加できなかったそうですが、いまは無事退院されて横浜公演では力強い歌を披露してくれました。

アンコールも最高

アンコールでは、ゲストとしてあんこう音頭の女こと佐咲紗花さんが登場して、最終章の前半3作の主題歌を担当することを発表し、オーケストラアレンジしたその主題歌を披露してくれました。

最終章の主題歌は、今までのガルパン主題歌のテイストとは少し違って早い曲調のロックテイストに感じました(今までの主題歌と歌っている人がそもそも違うから違う曲になるのは当たり前だし、そもそもオーケストラアレンジだから、なんとも言いようが無いけれど)。最高に楽しみです。

あと、なぜか最後にオーケストラ伴奏のEnter Enter MISSSONを会場のみんなでカラオケするという謎のイベントがあり、めっちゃ楽しかった。

最後はスタンディングオベーションの割れんばかりの拍手と共にオーケストラを見送り、おしまいでした。

最高

最高のライブイベントでした。最高なくらい最高なイベントでした。

サンキュー、ガルパンおじいさん。


カテゴリー: アニメ, ポエム | コメント / トラックバック: 0個

Elixirのパターンマッチ、不変性、型、演算子とwith式


教本は「プログラミングElixir」です

Elixirのパターンマッチ

Elixirでは、変数代入ではなく変数名にパターンマッチを使って値を束縛します。同じ関数型言語のHaskellや、Scalaのvalと同じような感じ。

束縛した変数と異なるマッチを行うと、例外が飛ぶ

変数aに1を束縛しているので、2とマッチさせようとして例外が飛んでいる。ただ、Haskellとは違って、変数に再度の束縛は可能

なんかユルくない? とおもうけど、まあこういうもんだと思ってスルー。束縛を維持したままマッチに利用するには、pin演算子(^)を利用する。pin演算子を使うことで、変数に値を再度束縛すること無く、束縛されている値とのパターンマッチを行う。

パターンマッチなので、左辺と値が一致してないマッチは例外が飛ぶ。何にでもマッチして値を利用しない記号は、_を使う。

不変性

Elixirは、immutableな値を扱う言語である。データが不変であるので、元の値から新しい値を作る時にコピーを取るが、Elixirは値が不変であることを知っているので、不必要なコピーを作成しないようにしてオーバーヘッドを抑えている。

例えば、リストはhead(配列の先頭要素)とtail(先頭以降の要素のリスト)に分かれていて、既存のリストに対して新しい要素をheadに加えたリストを作る場合は、このように書く。

この時Elixirは、listの値が不変だと知っているので、new_listには新しいhead要素である0を、tailにはlistへの参照を持つリストを作ることで、不要なデータのコピーを避けている。

Elixirにおけるデータの操作は、データの変換と捉えるとわかりやすい。

オブジェクト指向言語を習得した後に  String.capitalize str という式は微妙に感じるし、特に自分はRubyから来たので、 str.capitalize というメソッド呼び出しで元のstrを変更せずに変更した値を戻り値として受け取ることに違和感が無いが、それでもオブジェクトのメッセージ呼び出しは、オブジェクトに対してどんな影響を及ぼすのかが不確定で、プログラマに余計なことを考えさせる余地が多いので、Elixirや他の関数型言語では String.capitalize str のように明確にデータを変換するという書き方が良しとされる。

Elixirの型は、次のようなものがある

  • 値型
    • 任意の大きさの整数(integer)
    • 浮動小数点数(float)
    • アトム(atom)
    • 範囲(range)
    • 正規表現(regular-expression)
  • システム型
    • PIDとポート(port)
    • リファレンス(reference)
  • コレクション型
    • タプル(tuple)
    • リスト(list)
    • マップ(map)
    • バイナリ(binary)

これに加えて関数も型らしいが、教本ではここの一覧に入っていなかったのでとりあえず置いておく。文字列や構造体は、これらの基本的な型から組み立てられるらしいので、ここでは書かれていない。

IntegerとFloat

この2つに関しては、他の言語のそれらとよく似ているので特筆することは無さそう。整数に関しては、最大値というものは無いらしい。

Atom

アトムは、何かの名前を表現する型。説明をざっと読んだ感じ、Rubyのシンボルに近いのではないかと思う。コロンで始まる単語か、Elixirの演算子がアトムに該当する。コロンで始まり、ダブルクォートに囲まれた文字列も、アトムとして解釈される。

Range

start..end で表現される、範囲

Regular-expression

正規表現のリテラルで、~rで始まり、対になるセパレータで囲まれた文字列と、セパレータのあとに付けるオプションから構成される。セパレータは、正規表現の慣習で/が使われることが多いが、エスケープなどの手間で{}を使った方が読みやすい。が、個人的には//で囲まれていれば正規表現という共通認識がかなり強いので、ケースバイケースな気がする。Elixirの正規表現は、Perl5のPCREに準拠している。強い。

PIDとポート

PIDは別プロセスへの参照であり、ポートはIOリソースへの参照。自身のPIDはselfで取得できる。

リファレンス

この教本では扱わないらしい

タプル

順番を持ったコレクション。HaskellとかScalaとかでも出て来る。パターンマッチも利用でき、関数の戻り値として成否とリソースを持ったタプルを返すことがよくあるらしい。

最初の要素が、:okというアトムであるタプルを返す関数の例

リスト

リストは、[]で要素を囲む。Elixirでのリストは配列ではなく、連結データ構造である。不変性の項目で説明したheadとtailという話が関わってくる。先頭から直線的にデータを参照するのに効率的だが、ランダムアクセスに弱い。

リストには、連結演算子++や、差分演算子–、要素が存在するかを確認する演算子inがある。

キーワードリスト

キーと値の対のタプルを持つリスト(マップではない)は多用されるので、シンタックスシュガーが存在する。Rubyとよく似ており、2つの要素を持つタプルの1つ目の要素がアトムである場合は、この2つの式は同じ値を返す。

また、これもRubyと同様に、関数呼び出しの最後の要素がキーワードリストの場合、外側の[]を省略できる

マップ

マップのリテラルは、 %{key => val} で表現される。マップのキーはすべて同じ型であることが推奨されるが、異なっても構わない。ユルい気がするが、まあそういうものなのだろう。

キーがアトムの場合、リストと同じシンタックスシュガーが使える。また、キーには式が使用できる。

マップへのアクセスは[]を使用するが、キーがアトムの場合はドット演算子のシンタックスシュガーが使える。

マップとキーワードリストは非常によく似ているが、マップはキーがユニークであるのに対し、キーワードリストは同じキーを複数持つことが出来る。一般的に、マップは連想配列がほしい時に利用し、キーワードリストは関数やコマンドラインの引数に利用する。

バイナリ

バイナリリテラルは、<<>>で囲む。なんかこのへんはちょっとややこしそうなので、一通り学習してからまた考える。

真偽値

Elixirにおける真偽値は、true, false, nilの3つである。nilは、ブール演算においてはfalseと同じ働きをする。

演算子

演算子はたくさんあるらしいので、教本で取り上げられているものだけを見る

比較演算子

ブール演算子

算術演算子

算術演算子は、+, -, *, /, div, rem がある。
/は浮動小数点数を返し、divは除算の整数値、remは除算のあまりを返す。

連結演算子

in演算子

with式

Elixirのスコープはレキシカルスコープで、幾つかの構造はスコープを生み出す。内包表記で使われるforと、ここで出てくるwithは、それぞれスコープを作る。(forは後ろの章ででてくるらしい)

言語名、公開された年、現在の最新バージョンが書かれた次のようなCSVから、Elixirの項目を取り出す関数は、このように書ける。

実行結果

当たり前だが、外側のスコープのvalueが、withの中のvalueで書き換えられていることはない。with式の中で宣言された変数束縛は、doブロックの中でのみ有効になる。

上のスクリプトの中で、いくつか使われているパターンマッチのどれか一つでも失敗すると、MatchError例外が飛ぶ。例えば、languages.csvのオープンに失敗するとこうなる。

まあこれは良いとして、Elixirのエントリを探すパターンマッチでコケてこの例外が出るのはちょっと違和感があるので、with式の中のパターンマッチでは=の代わりに<-を使うことで、マッチできなかった時にマッチできなかった値を返す。

さっきの関数の、最後のパターンマッチを<-で書き換える。

実行結果は、例外ではなくnilを出力する

これは、Regex.runのマッチが失敗したときの戻り値がnilだかららしい。

また、with式は関数やマクロのような呼び出しらしく、with式と同じ行に式を書くか、もしくは括弧が必要となるらしい。

教本のP34では、doの前ではなくendのあとに閉じ括弧が書いてあったが、多分誤植だと思う。丸写ししたけど動かなかった。withがマクロなので、withの引数としてマッチをとり、それを閉じたあとにdoが続くと考えれば違和感が無い。

次回

4章までおわったので、次は5章からやっていきます。


カテゴリー: Elixir | コメント / トラックバック: 0個