Brainf*ck
書いた動機#
BigQuery 中心アーキテクチャでは、統計分析や機械学習のデータ準備を、Cloud Composer などから SQL を使ったデータ変換によって実現する。しかし、直列でデータ変換を行う場合には 標準 SQL のスクリプト で十分なケースもあり、開発をしてつらくないなら採用したい思いがあった。 先日のサイレントアップデートで、小さなクエリの高速化が行われたことから、十分な速度がでると予想した。 BigQuery で Brainf*ck を実現して、チューリング完全であることを確認したかった。欲を言えば SELECT 文だけで実現したいが、再帰 WITH 句が使えないことから自分には実現手段がわからなかった。スクリプトならできると思い、やってみた。
公開データセットに関数公開しました#
BigQuery スクリプトとは#
1 ジョブで複数のステートメントと制御構文を利用できる。 よく使うものだけ解説を入れる。
DECLARE#
変数宣言。DEFAULT を指定しないと NULL で初期化される。
SET#
変数代入。
IF#
条件分岐。
LOOP#
終了条件のない繰り返し。IF, BREAK と合わせて使う。
WHILE#
終了条件のある繰り返し。
BREAK#
繰り返しから抜ける。
CONTINUE#
今のブロックをスキップして、繰り返しの先頭に戻る。
RETURN#
スクリプトを終了する。早期リターンで使う。
CALL#
プロシージャを呼ぶ。構造化プログラミングに使う。
開発してみてつらかったこと#
- BigQuery のジョブ実行時間 6 時間制約が、スクリプト 1 つでかかってくるため、多段に時間がかかるクエリの実行は難しい
- クエリ履歴が大量の
CREATE TEMP FUNCTION __type_coercion_internal__
で汚染され、複数同時実行するとウェブ UI では結果が追えなくなる - ARRAY の 1 つの要素だけ書き換えるコストが高い
- スクリプトによるオーバヘッドがとても大きく感じる
- API から結果を回収できない
実行結果#
行 | f0_ |
---|---|
1 | Hello World! |
うまく動いていそう。
ソースコード#
再帰じゃない版#
再帰 PROCEDURE 版#
PROCEDURE で再帰を使ってきれいにかけると思ったのですが、エラーにより Hello World を動作させることはできませんでした。
Out of stack space due to deeply-nested procedure call to a.brainfuck
とのエラーなため、ネストの小さい記述は動きます。
おわりに#
BigQuery スクリプトで brainf*ck のインタプリタが実装できた。 再帰プロシージャの移植は不可能と結論づけました。再帰が可能なほど、スタックが詰めませんでした。再帰は 50 までの制約があります。