電通大のマイナー第二外国語

ツイートにするには長くなりすぎるのでどこに書こうか迷った結果ブログ記事にします。40分くらいで殴り書いた記事です。ちょこちょこ修正すると思います。

自分は電通大の二外(独語、中国語、露語、仏語、韓国朝鮮語)の中で、マイナーな方に分類される露仏韓の3言語の第一第二(通年2単位×3)を履修しました。
韓については選択韓国朝鮮語(通年2単位)と韓国朝鮮語演習(半期2単位)も履修しました。
成績の標語は仏語第一第二のみ優、他は全て秀です。
一言語しか履修していない人は相対的にその言語や授業を評価することが不可能かと思われるのですが、そこで複数を履修した自分だからこそ信憑性のある情報を提供できると考えています。以下、軽くそれぞれ紹介します。

韓国語(姜先生)

いきなりですが、必修韓国語に関しては自分が履修していた時から先生が変わってしまったので必修の授業に関しては特に言えることはありません。選択と、演習以降を担当している姜先生は変わっていないのでこちらだけ。
韓国語はとにかく文法が簡単です。しかも単語も結構雰囲気的に日本語と近かったりします。
次の文は日本語でどういう意味になると思いますか?勘で大丈夫です。

  • 교과서가 무료다(発音:キョグァソガ ムリョダ)

正解は、「教科書が無料だ」です。日本語の助詞の「~が」は韓国語でも「ガ」、文末の「~だ。」は韓国語でも「ダ」です。(というのは都合の良いパターンのみ紹介したもので、実際は大抵ここまでうまく行きません)

独語と中国語は履修していませんが、上の5言語の中では言語としては最も韓国語が簡単だろうと確信しています。

また、姜先生の授業について。基本的には先生の話を座って聞いてるだけです。政治や韓国の文化などに関する雑談の比重がかなり大きいです。というかほぼ雑談だけで終わったりします。
授業中にいきなり指されて問題の答えを求められたり音読させられたりということはありませんが、演習問題について先に数人指名されて、しばらくしてから各自黒板に書きに行く、ということは結構あります。
ただ、問題は難しいものではなく、時間も十分与えられる上、別に間違えたところで減点されたりするものではないので、授業の緊張感はほとんど無いです。
あとは宿題プリントがたまに課されたり、単語テストが学期に1,2回あったりします。(これについては後述)

成績評価は、期末試験が70%、その他が30%……らしいのですが、体感的には宿題プリントや単語テストがどれだけ評価に加えられているのか怪しいと思っています。
とても褒められたことではないのですが、自分は宿題プリントを半分くらい提出せず、単語テストも1回「0点」を出しながら、期末試験で体感ちょうど9割程度答えて(開示はしてません)秀が来ました。
他の履修者の様子を見ていても、全体的に「思ったより良い評価が来る」感じです。落とした人は見たことが無いです。

きつい点を挙げるなら、最初に全く未知の文字であるハングルを覚えるのは大変かもしれません。でも、頑張って最初の1,2ヶ月ですらすら読めるようになってしまえば後は勝ちルートです。
あと、単語の暗記量はだいぶ多いです。単語カードを用意しましょう。

仏語(坂井先生)

まず始めに、かなりしんどいです。

仏語は言語としても複雑で難しいです。鬼のように活用や不規則動詞が存在します。そしてびっくりするほどハイペースで範囲を進めます。その上、授業もランダムに当ててその場で問題に答えさせられたり、寝てたら減点されたり(これはあくまで噂)、緊張が抜けません。
定期試験も結構難しいです。辞書は持ち込み不可ですが、容赦なく難しめの単語や細かい文法を問うてきます。ちゃんと準備しないと無理。
ここまで言うと散々な評価のようですが、「真面目に二外を勉強したい」という方にはベストだと思います。
坂井先生はスパルタですが、教師としては非常に素晴らしい方です。説明も分かり易いです。
1年間履修して、最も充実感があったのは仏語だったと感じています。

また、新しく文字を覚える必要がほとんど無いという点で導入は楽です。
あと、発音や文法など、様々な点でロジカルに整った言語だと感じます。法則を理解すれば理詰めで頑張れます。理系には向いてるかも。

試験問題はほぼ全て教科書と授業で配布したプリントからそのまま出ます。ここが温情ポイント。もし教科書持ち込めば100点が取れるであろう試験です。

また、評価は決して厳しくないです。落単を本気で確信していた人も可が来たり、優秀な人には秀もちゃんと来ます。

露語(金沢先生)

時間割の関係で自分はこれを再履露語として履修したので、授業の評価はあまり参考にならないかもしれません。悪しからず。

授業の楽さは断トツでした。完全に座ってるだけです。指されたり前に出されたりということは一度もありません。
授業の最初の方はロシアトークが多いです。ロシア語の音楽を聴いたり、アニメのようなものを鑑賞したりという時間も多いです。
言語としてですが、だいぶ簡単です。
「私 家」と単語を並べるだけで「私は家に居ます。」になったり、そのままアクセントを変えるだけで「私は家に居ますか?」にも「家に居るのは私ですか?」にもなったり、結構独特で面白いです。

つまづくとすれば、韓国語同様に新しい文字を覚える必要があります。ハングルよりは英語のアルファベットと近い物が多くて覚えやすいですが。

再履クラスだからかもしれませんが、評価は相当優しかったようです。


以上。

2nd Asprova Programming Contest 参加記

AtCoder上で行われた2nd Asprova Programming Contestに参加して、最終結果84人中10位でした。マラソン系のコンテストは初挑戦でした。記念に振り返りを書き残しておきます。なお、このコンテスト終了直後にTopCoderのMM106にシフトした結果1週間以上経ってしまっているのでかなり記憶があやふやです。

参加(12/28)

ratedのコンテストあるかな~と「予定されたコンテスト」欄を眺めるたびに目に入っていた謎のコンテスト。ページを覗いてみると期間が数週間あって、出来るだけ得点を最大化するいわゆるマラソン系のコンテストらしい。そして問題がめちゃくちゃ面倒そう。しばらくは全く参加する気がありませんでした。
しかしICFPCやマラソンマッチの紹介記事などを見て前からマラソン自体には興味があったので、冬休みでそこまでやることもないしということで意を決して飛び込むことに。

問題を把握(12/30)

問題はこちら。
https://atcoder.jp/contests/asprocon2/tasks/asprova2018_a

問題文は本当に長くて制約も面倒なのですが、辛うじて一文でまとめるなら「複数の工程から製造される製品とそのそれぞれの工程を担当できる機械などの情報が与えられるので、納期や数量、着手可能時刻が指定された大量のオーダーを効率良く捌くプログラムを作れ」というもの。
実際にはさらに様々なパラメータがあって、スコアの計算式(重視される要素)もテストケース毎に指定されます。
いきなりコードを書ける状態ではなかったので、2日間はどのようなアルゴリズムが使えそうかひたすら考えていました。

初提出(12/30)

まず考えられた解法は、時刻T=0からタイムステップを進めて行って、着手可能な状態のオーダーについて納期などの優先度で待ち行列を保持し、それぞれ使う機械を決めて順番に製造ラインに放り込んでいく愚直なシミュレーションです。
親切にも貪欲法によるサンプルプログラムが用意されていたので、これを軽く書き換えて手元でテスト。結構あっさり思い通りに動いてくれたので、提出してみる。4110億点くらいで、サンプルのままの点数(4083億点くらい)より若干高い程度でした。まあこんなもんかなという気持ち。

推敲

シミュレーションは当然現実に近づけた方が正確だからタイムステップは1(単位時間)にするべきだろうと思っていたのですが、TLEするので仕方なく適当に大きく取ってみたらかなり伸びました。4460億点に到達。

また、この問題では「段取り時間」がなかなか重要で、各機械ごとに直前に製造していた品目との相性によって段取り時間(無駄な余白)が大きく変わります。つまり、処理するオーダーの順番以外に、「どの機械に担当させるか」を吟味しなければなりません。
サンプルを流用したままだとこれが何も考えられていない(その工程を担当できる機械ならなんでもOK状態)なので、最も相性が良い機械を選ばせるようにしました。
4466億点まで上昇。(12/30)

あと、Pythonでビジュアライザらしきものを作ってみました。スケジュールからこんな感じの画像を生成します。
f:id:mino-k:20190116001827p:plain

使い勝手は良いと言えるものではありませんでしたが、どんなところが無駄になっているか、などを把握するのは事足りていたので結局最後までこのまま使い続けました。

さらに、着手可能になったオーダーを並ばせる順序は単に納期だけを基準にすると雑すぎるので、残りの工程数など要素を複雑に足して、それぞれに定数のパラメータを設定しました。
このパラメータは問題設定によってどれが最適かはかなり変わってくるので、それぞれいろいろに変えながら数十パターンでシミュレーションを回し、もっともスコアが良かったものを解として採用させることに。(1パターンのシミュレーションは最大数十msで済む)

また、解を作り終えた後、せっかく2000msの時間が与えられているのだからギリギリまで自明な改善点は改善させればいいのでは?と考えました。
ラソン勢からは突っ込まれてしまいそうですが、この時までの私は、マラソンマッチにおいて探索&ループの繰り返しが常套手段であることを知らず、解析的に一発生成して終わりだと思っていました。山登り法や焼きなまし法という概念も知りません。再発明の状態です。
各機械上のスケジュールの適当な隣接部分を入れ替えて時刻を再生成し、スコアを求めて元より良くなっていればそれを採用(いわゆる山登り法)としたところ、4490億点の壁を越えました。(12/31)

あとはこれを、単純なランダム選択以外に改善が見込めそうな箇所を探して操作したり、使う機械を入れ替えたりという操作を適当な配分で織り交ぜながら各箇所の処理をチューニングし、4493億点まで到達。(1/5)

その後伸び悩んでいたのですが、初期スケジュールの生成のたった一箇所の条件式を2文字変えてみたら4497億点に飛躍。(1/6) 数日前なら入賞圏内の得点でした。

感想

目標は5位以内入賞だったので力及ばず悔しいですが、何より楽しかったです。
普段のAtCoderのコンテストは多くて2,3通りの想定解法と1通りの解答が用意されていますが、マラソンは出題側さえどんなプログラムが高得点を出してくるか考えていないであろうし、競技者もそれぞれバラバラなアプローチを取れるところがワクワクします。
冒頭にも書いた通りこのコンテストをきっかけにマラソンマッチに目覚め、TopCoderにも手を出し始めました。
TopCoderMM赤を真剣に目指していきます。がんばります!

電通大学食テレビの紹介

こちらはUEC Advent Calendar 2018 - Adventarの23日目の記事です。
前日はaskjfbd (@askjfbd) | Twitterさんの再帰関数には2種類あるって知ってました? / (時間があれば)Lispを布教してみる - askjfbdの第二のにっきでした。
末尾再帰については1,2年の授業でも何度か話題に上がりますね。お世辞抜きに18生は技術的に素敵な方が多くて恐れ慄いています。


はじめに

皆さん、学食使ってますか?私は全くと言っていいほど使いません。
そんな私が所属する「学食テレビ」についてここでは紹介します。

学食テレビとは

学食テレビって聞いてどんな組織か、はっきり答えられる電通大生は少ないのではないのでしょうか。私も自信がないです。

ホームページの説明を見ると、

電気通信大学 学食テレビは、本学の食堂に設置したディスプレイを使って学生同士の交流や学内の情報共有を活発にするプロジェクトです。

ということです。これは8割方嘘です。
名前や建前上の説明からは高校までの放送委員会のような何かをイメージするのではないかと思います。事実、数年前までは生放送番組を企画したり「学食テレビ」らしい活動をしていた*1そうなのですが、現在は件のディスプレイを使う活動はほとんどしていません。名前だけ残っています。*2
では今はどんなことをしているかと言うと、事実上の準技術系サークルです。
Ⅰ類CSの成見先生が統括指導をして下さりながら、数名の学生スタッフが好きに活動しています。

やってること

具体的にどんなことをやっているのか聞くと、割と「あー、あれってそうだったの!」となる物も多いのではないでしょうか。今年の活動の一部を挙げます。

冬季のTwitter連携イルミネーション(#UEC_LIGHT)

2年前から、この季節になるとメインストリート沿いの電柱にイルミネーションを設置しています。

#UEC_LIGHT とタグを付けて何かツイートすると文面に応じて点灯したり文字が流れる、というものです。
イルミネーション自体が寂しいということで昨年まではあまり良い評判が得られなかったみたいですが、文字が流れる今年は中々遊んでもらえているようです。

U Explore Canvas( / VRChatUECワールド)

今年はVRChatに電気通信大学の再現ワールドがアップロードされて話題になりました。

実はこのワールドは元々学食テレビの企画でチーム開発していたUExCというゲームのために作られたものです。iOS版とAndroid版と公開されているので、まだ遊んでいない方は是非インストールしてみてください。*3

UExploreCanvas

UExploreCanvas

  • Haruka Hoyama
  • ゲーム
  • 無料
play.google.comこれについては後述。

MIKUEC連携

バーチャルライブ研究会さんのMIKUECで、初音ミクになって鏡写しで踊れるという技術デモを展示したりしました。成見研究室としてエンディングにも名前が載っていましたね。


普段の活動

基本的に週1回、成見研究室でミーティングをおこなっています。(長期休みは不定期)
技術系サークルで言うところの進捗報告会にあたるのでしょうが、適当にお茶を飲みながらメンバーの誰かが作ったものを見て「うわ~すげぇ!」と盛り上がったり、VTuberの動画をみんなで観賞したり、適当に集まっているだけです。

終わりに

学食テレビは本当に掴みどころのない組織で、部費なども当然なく、正式な入会(?)や脱退の手続きも存在しません。
いつでも新しいメンバーを募集しているので、興味があったら私か公式問合せフォームからでも声掛けてください!また、技術的に何かしたい訳ではないが顔出し上等で生放送番組を復活させたい!というような方も歓迎です。
ここまで読んで下さりありがとうございました。

24日目の記事は音の兔🐰ウサウサラビットライフ🐇 (@nenou_sa) | Twitterさんが書いて下さるようです。
実は私、彼とは2回ほど一緒にハッカソンに出たことがある程度の関係だったりします。大学の同期で技術的に尊敬している人の1人です。期待していきます。


おまけ:私が入ってからやったこと

さらっと読める何か、という説明で今日まで来てしまったので記事の本体はこの上までということにしますが、私が学食テレビに入った経緯と今日までどんなことをやってきたかを語ります。物好きな方はどうぞ。

入るまで(2018年2月頃)

私は1年生の時、情報工学工房を取っていました。UnityとFPGAの班の指導教員が成見先生だったのですが、私はFPGAを取っていたので、1年間お世話になっていました。*4
情報工学工房は2月に全テーマ合同で最終発表会をおこないますが、そこで最後、解散する直前に成見先生がUnityとFPGAの班のメンバー10名程度に「学食テレビっていうのをやってるんだけど、興味があったら次回のミーティングに来ませんか?」と訊いて回ってらっしゃた訳です。コネを用いた宣伝
チョロい私は「(えっそんな面白そうなとこに入る機会をくれるの!?)」とノータイムで行きます宣言をしました。実際にミーティングに行ってみると工房履修者の中で来ていたのは自分だけで、ズレを感じました。

ちなみに最初は「体験で見学」という話だったのですが1回目の流れで2回、3回とミーティングに出ていたらヌルッといつの間に正式メンバー扱いになっていました。

3D CAD(~2018年6月頃)

始め、成見先生から「最近なんかやりたいことある?」と大変ファンキーな質問を頂きました。それはもう無限に挙げられますが、先生に向かって言えるものとなるとこれが実は思ったより浮かばないもので、口をつぐんでしまいました。
それならこんなことをやってほしいんだけど……ということで頼まれたのが、スタンドアロンな電光掲示板をちょうどいい感じに固定するための構造部品のデータを作ってほしいというものです。
機械系の知識はほとんど無かったので最初は苦戦しましたが、相当いい経験になりました。自分が設計したものが形としてそのまま出力されるって楽しいもんですね。

U Explore Canvas(2018年7月頃~)

上でも挙げたモバイル用ゲームです。

今年は電通大100周年ということで、それに合わせて電通大を舞台にしたゲームを作りたいと成見先生が仰られ、それを発端に動き出しました。
参加した他のメンバーはミニゲーム担当が3人(内1人がリーダー)、モデルや音楽担当が1人です。
私はゲームやマップを作ったという訳ではなく、このゲームの一つの核である「展示機能」を担当しました。
どういった機能かと言うと……

  1. Twitterで「#kodo_art」とタグを付けて画像を呟く
  2. ゲーム内で適当なキャンバスの前に近づき、設置ボタンを押す
  3. キャンバスに自分の好きな画像が展示される!(他プレイヤーのマップにも動的に反映される)

というものです。本当はこれをもう少し拡張してリアルタイムなオンラインゲームにしようかとか考えてたんですけど力尽きました。Unity難しいです。

情報領域演習サポート(2018年10月~)

情報領域演習(第一~第三)というのはⅠ類のとある必修科目で、原則欠席即不可という無慈悲な制度に加えてその演習課題の難易度から大変なヘイトを集めています。特に第三はかなり落単率が高いようです。
そこで成見先生が「難しい情報領域演習で全然問題を解けずに落としたり、友達のコードを写させてもらい続けて単位を取ったはいいが結局後々力不足で留年する学生が多いのでなんとかしたい」ということでこの計画が立ち上がりました。
もともと別件で「UEC奨学生の仕事が少なすぎるのでもう少し色々活動してもらってもいいんじゃないか」ということを成見先生が考えていたこともあり、ちょうどUEC奨学生だった私は喜んでとりあえず1人目の先生(?)になることに。他の奨学生にも声を掛けたそうなのですが結局引き受けたのは私だけだったみたいです。デジャブ

ということで、毎週月曜5限にCEDで張り切って先生をしていますので困った方はお気軽に来てください。答えを教えてもらうのではなく、基本は自習で何か訊きたいことがあったらわかる人が直接1対1で教える、という場です。

以上、ここまで読んで下さった方は本当にありがとうございました。

*1:当時の司会の方のブログがこちら UEC学食テレビ司会の主観ブログ

*2:番組企画が消滅した理由は、「放送に出たがる学生がそうそういない」とのこと

*3:Google Playでの制作者が私になっているのはたまたま私が開発者アカウントを持っていたから。

*4:ちなみにここで私は、「FPGA上にライフゲームのハードウェアを設計してそのライフゲーム上で論理回路を動作させる」というよくわからない研究をしました

プログラミング言語開発記Ⅱ - 字句解析

冗談抜きに多忙でしばらく開発がストップしていました。失踪なんてしていません。しませんよ。


1記事目はざっくりとした概要しか書けなかったので、そろそろ具体的な処理にも言及していきます。ちなみに、もう気付いてくれた方もいるかもしれませんが今回から文体がですます調です。公式記事ではなく個人ブログならもっと軽く適当に書いてもいいかなと考え直したので。

一般的にプログラミング言語の処理系がどういった流れで処理しているのかについてはあまり詳しくないのですが、大体これは王道が決まっているかと思います。Laiskは次のような流れです。

Laiskソースコード(string)

↓ < 字句解析(トークンへの切り分け・トークン種別の判定)

トークン列(vector<token>)

↓ < 構文解析①(操車場アルゴリズムの応用)

トークン列(vector<token>)

↓ < 構文解析②(構文木の構築)

構文木(node)

↓ < 書き下し(深さ優先探索のような何か)

C++ソースコード(string)

この記事では最初の処理である、字句解析について述べます。

今回私が実装した字句解析関数 vector<token> lex(string code) とその補助関数がこんな感じ。
ちなみに、pair<int,string> をtypetokenとしてtypedefしています。first要素がトークン種別(マクロ定数)、second要素が実体の文字列です。

// define types of characters
string bracket_open = "({";
string bracket_close = ")}";
string separator = " ;,\n";
string quotation = "\"\'";
string myoperator = "=+-*/%~";
string number = "0123456789.";
string name_part = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_";

int char_type(char c) { // character -> character type
	if (name_part.find(c) != string::npos) {
		return NAME_PART;
	}
	else if (separator.find(c) != string::npos) {
		return SEPARATOR;
	}
	else if (quotation.find(c) != string::npos) {
		return QUOTATION;
	}
	else if (bracket_open.find(c) != string::npos) {
		return BRACKET_OPEN;
	}
	else if (bracket_close.find(c) != string::npos) {
		return BRACKET_CLOSE;
	}
	else if (number.find(c) != string::npos) {
		return NUMBER;
	}
	else {
		return OPERATOR;
	}
};

map<int, int> char_to_token = // character type -> token type
{
	{NAME_PART,T_NAME},
	{OPERATOR,T_OPERATOR},
	{QUOTATION,T_STRING},
	{BRACKET_OPEN,T_BRACKET_OPEN},
	{BRACKET_CLOSE,T_BRACKET_CLOSE},
	{SEPARATOR,T_SEPARATOR},
	{NUMBER,T_VALUE}
};


vector<typetoken> lex(string code) { // lexical analyze
	int state = T_INITIAL;
	vector<typetoken> result;
	string token = "";
	for (int i = 0; i < code.size(); i++) {
		char c = code[i];
		char type = char_type(c);
		switch (state) {
		case T_INITIAL:
			token += c;
			state = char_to_token[type];
			break;
		case T_NAME:
			switch (type) {
			case NAME_PART:
			case NUMBER:
				token += c;
				break;
			default:
				result.push_back(typetoken(state, token));
				token = c;
				state = char_to_token[type];
				break;
			}
			break;
		case T_STRING:
			switch (type) {
			case QUOTATION:
				token += c;
				result.push_back(typetoken(state, token));
				token = "";
				state = T_INITIAL;
				break;
			default:
				token += c;
				break;
			}
			break;
		case T_VALUE:
			switch (type) {
			case NUMBER:
				token += c;
				break;
			default:
				result.push_back(typetoken(state, token));
				token = c;
				state = char_to_token[type];
				break;
			}
			break;
		case T_OPERATOR:
			switch (type) {
			case OPERATOR:
				token += c;
				break;
			default:
				result.push_back(typetoken(state, token));
				token = c;
				state = char_to_token[type];
				break;
			}
			break;
		default:
			result.push_back(typetoken(state, token));
			token = c;
			state = char_to_token[type];
			break;
		}
	}
	return result;
}

lex関数が処理中で内部的に保持するのは、最終的に出力するトークン列(result)、作成途中のトークン(token)、作成しているトークンの状態(state)のみです。

tokenは空文字列、stateはINITIALの状態から順番にコードのi文字目cを受け取り、stateとcの文字種別に応じて処理を変えながらresultに放り込んでいくだけです。


例えば、b=getint+2; というコードを受け取ったとすると

①stateはT_INITIAL、1文字目の'b'はNAME文字なのでtokenは"b"、stateはT_NAMEへ遷移
②stateはT_NAME、2文字目の'='はOPERATOR文字なので保持しているtoken("b")をresultにpushしてtokenは"="に、stateはT_OPERATORに遷移
③stateはT_OPERATOR、3文字目の'g'はNAME文字なので保持しているtoken("=")をresultにpushしてtokenは"g"に、stateはT_NAMEに遷移
④stateはT_NAME、4文字目の'e'はNAME文字なので保持しているtoken("g")に'e'を追加してtokenは"ge"に遷移
⑤stateはT_NAME、5文字目の't'はNAME文字なので保持しているtoken("ge")に't'を追加してtokenは"get"に遷移
……
同様に続く
……
⑥stateはT_NAME、9文字目の'+'はOPERATOR文字なので保持しているtoken("getint")をresultにpushしてtokenは"+"に、stateはT_OPERATORに遷移
⑦stateはT_OPERATOR、10文字目の'2'はNUMBER文字なので保持しているtoken("=")をresultにpushしてtokenは"2"に、stateはT_VALUEに遷移
⑧stateはT_VALUE、11文字目の';'はSEPARATOR文字なので保持しているtoken("2")をresultにpushしてtokenは""に、stateはT_SEPARATORに遷移

といった具合で切り分けられます。
少し考えればわかりますが、このままのコードでは例えば"a=++b"のようなコードがうまく分解できません。OPERATOR文字が続いていれば一塊として捉えてしまうのです。今回はあくまで雛形を示しているので気にしないことにします。

定数等をまとめているconstants.cppも一応示します。

#include<fstream>
#include<iostream>
#include<map>
#include<sstream>
#include<stack>
#include<string>
#include<vector>
#define T_INITIAL 0
#define T_NAME 1
#define T_STRING 2
#define T_VALUE 3
#define T_BRACKET_OPEN 4
#define T_BRACKET_CLOSE 5
#define T_SEPARATOR 6
#define T_OPERATOR 7
#define NAME_PART 0
#define OPERATOR 1
#define QUOTATION 2
#define BRACKET_OPEN 3
#define BRACKET_CLOSE 4
#define SEPARATOR 5
#define NUMBER 6

using namespace std;

typedef pair<int, string> typetoken;

マクロ定数以外にもいろいろ突っ込んでて相当お行儀が悪いですね。

lexを動作させる例がこんな感じ。

#include "constants.cpp"
#include "lex.cpp"

int main(int argc, char** argv) {
	ifstream ifs(argv[1]);
	stringstream strs;
	strs << ifs.rdbuf();
	string code(strs.str()); // read Laisk source code
	vector<typetoken> tokens = lex(code); // lexical analyze
	for (typetoken tok : tokens) {
		cout << tok.second << " "; // show result of lex
	}
	cout << endl;
}

これに、例えば次のようなファイルを与えると

n=getint;b=getints(n);put(max(b)*2);

次のように空白で切り分けて出力され、めでたく字句解析が出来ました。

n = getint ; b = getints ( n ) ; put ( max ( b ) * 2 )

実用上はこのままではとても動かせませんが大まかに実装したアルゴリズムの説明ということで。本当は構文解析も大体出来てるんですけど今回はこれくらいにします。

プログラミング言語開発記Ⅰ - 概要

少し前から自作のプログラミング言語、Laiskを開発している。自身の思考の整理や今後似た試みに手を出す方への助けとなればと思い開発記録をブログ記事として書き残していく。

発端

今学期に私が大学で履修している講義で、木構造による数式の表現に面白さを覚えたこと。その直前に参加したAtCoderのコンテストでRubyによる競技プログラミングに速度面の限界を感じたこと。

ちゃんとC++コーディングを練習しなければなあと思いつつもどうしても面倒臭かったので、いっそプログラムにプログラムを吐かせてしまったらどうかという発想である。ものぐさの境地といったところであるが、常に「技術の発展は怠惰から」と私は考えている。

概要

第一の特徴に、LaiskコードはC++ソースコードコンパイルされる形で実行可能となる。この形式を取る2つの大きな利点として、機械語アセンブリ言語に直接触れないまま既存のプログラム資源を有効活用して高速にプログラムを実行できることと、あくまでC++コードを生成するためオンラインジャッジで提出可能であること、があるかと思う。発端が競技プログラミングということもあり、言語よりは「自分で書き貯める競プロライブラリの延長」という感覚に近い。

その他、いくつか言語仕様の特徴を挙げる。

    • 突き詰められた式指向・関数型言語

      Laiskコードはパースされた結果、"root"関数で括られて引数に関数の入れ子が繰り返された巨大な1つの木構造となる。ifやwhileや代入など全ての関数はそれぞれの規則での明確な返り値を持っている。
    • 短い組み込み関数名、豊富な糖衣構文

      元々記述を楽にするために作られているため、組み込み関数の名前は意味が判別可能な範囲で短くされている。また、例えば「0~100」は0から99まで100個の整数が順番に入った配列を生成するというように直感的な記述が可能である。
    • 変数は宣言不要、全て動的型(要検討)

      新出の変数は勝手にその場で宣言される。さらに、C++のライブラリであるboost::anyを利用し、スクリプト言語のような動的型を実現する。ただし、パフォーマンス面等も考えて今後検討していく。

コード例

以下にLaiskコードの例としてフィボナッチ数列の先頭100項を空白区切りで出力するものを挙げるが、あくまで理想的な完成像に基づいている。

(f=0~100).map!(i){i<2?i:f[i-2]+f[i-1]}.put

現状

現時点でそれなりに開発を進めて来ていたのだが、このたった2週間で私の無計画さが如実に顕れた紆余曲折を経ている。

  • Webページ上でコンパイルできると楽そうなのでJavaScriptで書き始める
  • 軽く形になったが生成コードの美しさに納得が行かずC++生成方式を変えてほぼ一から書き直す
  • ようやく良い感じになったが後々のことを考えるとコンパイラ自体もC++で書いた方がよいのでは?と考え一からC++で書き始める
  • せっかくなのでコード生成方式も全く別スタイルに切り替えよう←今ココ

要は、結局まだ(約3回目の)振り出しである。C++を勉強したくないが為にC++コードを生成するコンパイラC++で書くというわけのわからないことになっているが突っ込んではいけない。これから詰まった点や進んだ点を少しずつこの場で報告しければと思う。