traP Member's Blog

Amazon Prime Musicおすすめ5選 & minimalなプログラミング言語「miniscript」の紹介

long_long_float
このエントリーをはてなブックマークに追加

これは、traP Advent Calendar 2016 5日目の記事です。

どうも、@long_long_floatです。最近はTitanfall2にドハマりしています。メカがかっこ良すぎる…過疎気味なのがアレですけど。

Amazon Prime Musicおすすめ5選

Amazon Prime Musicをご存知ですか? これは月額325円(学生は158円)の音楽聴き放題サービスです。これに加入するとおまけでAmazon Prime ビデオが見放題になったり、お急ぎ便が無料になったりします。アドです。

自分は以前からAmazon Prime会員だったにもかかわらず、あまり聴きたい曲がなかったのでAmazon Prime Musicを使っていませんでした。ところが最近になって改めて検索してみると自分好みの曲がザクザクあったので、作業中はヘビロテしています。その中でこれはおすすめという曲を5曲紹介します。

ジャンルが偏っている気がしますが気のせいです。

迷惑メーリングGIRL

毎日のように受け取るであろう(フィルタで目につかないかもしれませんが)迷惑メールをいい感じに皮肉った曲です。「いくつあるの?国内最大級サイト」「これで最後っていつも言うのさ」など迷惑メールあるあるを上手く歌詞にしています。あと、歌詞カードが迷惑メールっぽくて面白いです(リンク先は歌詞の一部です)。

めいら〜だえもん☆だもん

上と同じく迷惑メールをテーマにした曲です。電波度が高いです。「404」「速攻返送 そう MAILER DAEMON」「POPがMIMEでSMTP」等知っている人が聞けばニヤリとできるフレーズが入っています。他の曲とかを聞いてても思うのですがそういうフレーズを歌詞に入れるのが上手いなぁと。知っている人が聞いても違和感がないのは良いです。

ウサキック!

友人にCDを借りて初めてななひらを聞いたのがこのアルバムのこの曲です。今までこういうジャンル(電波ソングというのでしょうか)を聞いたことがないので衝撃を受けました。低音が効いていながら軽快なミュージックは聞いてて癖になります。聴き過ぎると脳内ループするというリスクはありますが。

ファッとして桃源郷

ファッ!? てーきゅうというテニスアニメ4期の主題歌です。アニメと同様、早口で軽快な感じです。ニコニコ動画にもそこそこMADがあり面白いので、一度見ておくと良いです。この前まではAmazonビデオでてーきゅうが見られたのですがさっき見てみたらプライムからは消滅してました。残念。

ミス・パラレルワールド

上の曲とは違い、落ち着いた感じの曲です。軽快(語彙不足)なリズムに意味不明な歌詞、柔らかい声、そして嫌というほど繰り返される「パラレルワールド」というフレーズで中毒になります。ボーカルはやくしまるえつこで、この曲で気になった方はどんどん他の曲を買ってみることをおすすめします(Amazon Prime Musicにはあまりないので)。

以上、おすすめの曲でした。もっと挙げたいのですが無限に出てきそうなので5選にとどめておきます。Amazon Prime Musicを聴いて作業効率を爆上げしような👊

minimalなプログラミング言語「miniscript」の紹介

https://github.com/long-long-float/miniscript

上だけだと技術の片鱗もないので最近作ったプログラミング言語の紹介でもします。minimalと言っていますが、世界最小とかではないです。

きっかけ

部内のWikiがあって、JavaScriptを埋め込むことができたので、そこの自己紹介の記事で動くプログラミング言語(自己紹介がプログラムで書いてあって実行すると自己紹介が出力されるようにしたかった)があったら面白いなぁと思って講義時間で作りました。完成していざ実行してみようとしたらWikiがいつの間にかJavaScriptが実行できなくなっていたので結局日の目を見ることは無くなりました。

言語仕様

できるだけ手間を掛けたくなかったので小さくしたかったのでシンタックスはJSONまんまです。どういうことかというとLISPみたく、式を配列(リスト)で表現できるようにしました。また、数値や文字列などの値もJSON準拠です。なんか最近バズったorelangに結構近いですが狙ってないです。

シンタックスをJSONにしたことによる弊害は、

  • 表現(式や文を含む)をすべてJSON(JavaScript)の値にしなければいけない
    • 特に変数参照(というかシンボル)がそのまま書けないので文字列になっています。これだと普通の文字列と区別がつかないので以下に述べるように関数参照で解決しました。
  • とにかく書きにくい
    • ケツカンマが許されない
      • 上と関連することですがケツカンマ(配列等の末尾に,をつけること)が許されないのでコードを書いているとイライラする時があります。
  • 見た目が不格好になる

これに対して、利点は

  • 構文解析が楽(というか何もしなくていい)

これに尽きます1。そして実用的な言語を作るにあたって上の利点は欠点を凌駕しません。ちゃんと構文解析器作ろうな。

構文と呼べるものは[funname, args…] (関数・特殊形式の適用)と無名関数(クロージャです)、数値、文字列などの値のみです。変数への代入やif式は関数か特殊形式になっています。特殊形式とはマクロみたいなものでそれ自体が評価される時点では引数の評価が行われないものを言います。例えばif式はconditionが真なのにelseが評価されたら困るので、特殊形式になっています。JSONがシンボル的な識別子を表現できないので関数適用にしてしまっています(例えば変数n["n"]となります)。あと変なところは、トップレベルで複数の式が書けないので無名関数の中に式書いてそれ自体を適用してねということぐらいでしょうか。

言語仕様はこれだけです。

FizzBuzz

では早速、十八番2であるところのFizzBuzzを書いてみます。

["for", ["range", 1, 100], ["do", ["n"],
  ["print", ["switch",
    ["=", ["%", ["n"], 15], 0], "FizzBuzz",
    ["=", ["%", ["n"], 3], 0],  "Fizz",
    ["=", ["%", ["n"], 5], 0],  "Buzz",
    ["n"]
  ]]
]]

どうでしょうか。ぎりぎり読めそうじゃないですか? 以下適当に解説します。

  • "for"はArrayかObjectを受け取ってその要素を順々に無名関数に渡していきます。型についてはガバガバなので(某氏に殺されそう)、引数が無かったり余分にある無名関数を渡しても普通に動きます。
  • "range"は閉区間(Array)を生成します
  • "do"は無名関数です。1つ目の引数に引数名、2つ目以降に式です。
  • "switch"はLISPでいうところのcondです。つまり偶数番目の引数を評価して真だった場合はその次の式を評価します。全て偽だった場合は最後の式を評価します。

実装について

実装は https://github.com/long-long-float/miniscript/blob/master/main.js を見て欲しいのですが、わずか100行弱です。しかもほとんどは組み込み関数・特殊形式の実装なので背骨としては50行くらいだと思います3 (やっていることとしては再帰的に配列を辿っていって末端(葉)にぶつかったら処理している感じです)。ここではややこしいクロージャについて解説します。

クロージャについて

クロージャとはソースコードの見たまんまのスコープ(レキシカルスコープといいます)を持つ無名関数のことで、要するに無名関数の中から外の変数を参照できる無名関数です。そんなの無名関数だったら当たり前かと思われるかもしれませんがレキシカルスコープではない無名関数である言語があるんですよ(古いJavaとか)。

miniscriptではこれを実現するために無名関数にlparent(Environmentが入ります)を持たせています。lparentはLexical Parentの略です。Lexicalというのは「構文上の」みたいな意味です。Environmentとは環境、つまり現在実行しているコンテキストの情報(miniscriptでは変数と親のEnvironmentを持たせています)のことです。

Environmentはスタックとして保持され、関数が実行されるたびに新たにpushされます。そして関数実行が終わった時点でpopされます。こうすることで例えば別の関数で同じ変数をsetしている場合や再帰関数に対応しています。

さて、無名関数は評価された時点でlparentに現在のEnvironmentが格納されます(下図左)。そしてその無名関数が実行されるときにその時のEnvironmentのparentlparentを設定します(下図右)。

[["do", [],
  ["set", "n", 1],
  [["do", [], ["print", ["n"]]]]
]]
figure1

figure1

でここからがポイントなのですが、["print", ["n"]]のコンテキスト(上図右下)には変数nは存在しないですよね? クロージャがない言語ではここで変数がないよーっていうエラーが出るところですがminiscriptでは現在の環境に変数がない場合はparentをたどって再帰的に検索します。これによって一個上の変数nが参照され無事1が出力されることとなります。

parentをたどっていくので以下のコードも直感的に動きます(C++とかのブロック文みたいな使い方ができる)。

[["do", [],
  ["set", "m", 0],
  ["set", "n", 1],
  [["do", [],
    ["set", "n", 2],
    [["do", [],
      ["print", ["m"]],
      ["print", ["n"]]
    ]]
  ]]
]]

出力

0
2

figure2

figure2

さて、クロージャを実装するにあたって重要な点が1つあります。先ほど、

無名関数は評価された時点でlparentに現在のEnvironmentが格納されます

と書きましたが、素朴に代入してしまうと以下のようなケースで誤った出力をしてしまいます。

[["do", [],
  ["set", "makeCounter", ["do", [],
    ["set", "n", 0],
    ["do", [],
      ["set", "n", ["+", ["n"], 1]],
      ["n"]
    ]
  ]],

  ["set", "counter", [["makeCounter"]]],
  ["print", [["counter"]]],
  ["print", [["counter"]]],

  ["set", "counter2", [["makeCounter"]]],
  ["print", [["counter2"]]], <---------------------- (a)
  ["print", [["counter2"]]],

  ["print", [["counter"]]],
  ["print", [["counter2"]]]
]]

出力

1
2
3
4
5
6

本当は(a)の時点で1が表示されているはずです4。これはJavaScriptのオブジェクトの代入が参照渡しになっているので、Environmentが共有されてしまっているのです。

figure3

figure3

Environment(と無名関数)をクローン(複製)してやれば正しく動きます。

figure4

figure4

まとめ

この記事を通して言いたいことはクオリティにこだわりさえしなければ(そしてバグを踏まなければ)プログラム言語なんてさくっと出来てしまう5ということです。なので興味があったら作ってみるといいですよ6

明日の担当はtraPの全強7こと、Dekaさんとpoppon_seadragonさんです。期待してどうぞ。


  1. 構文解析については12日目にleoさんが書いてくれるのでそちらに期待しておいてください

  2. FizzBuzzには繰り返し、条件分岐、出力と言ったプログラム言語の基本と言っていい機能を使うので個人的に言語処理系を作る時の最初の目標にしています。

  3. エラー処理は最低限しか書いていません。エラー処理を突き詰めようとするとプログラムが複雑になるし行数が増えてしまうのであまり書きたくない…。エラー処理を書かなくて良い未来が来て欲しい。

  4. クロージャの挙動については https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures が詳しいです

  5. miniscriptを作成するにあたって前提としている知識は配列や連想配列、木といったデータ構造、再帰等のアルゴリズムですかね。あと、GCとかめんどくさいことはJavaScriptの処理系に任せているのでコンパクトにまとまっています。

  6. 現実に使われている処理系がこんなにチープなわけが無いですが、本質としてはこんなものだと思いますし一度作ってみるとプログラミング言語に対する見方が変わるかもしれません(適当)。

  7. "任意の分野において高い能力を発揮する人のこと" (部内Wikiより)

このエントリーをはてなブックマークに追加

コメントを残す

メールアドレスが公開されることはありません。