JSON処理のコマンドラインツールであるjqは、JSONテキストを1行でちゃくっと解析、変換するときに使うものです。正統的なプログラミング言語ではないので、これでアルゴリズムの学習をすることはまずありません。
とは言え、jqにも変数定義などプログラミング言語らしき機能が備わっているので、「やればできんじゃねぇ」と野望を募らせてしまうこともあります。
今日のお題は、アルゴリズムの教本には必ず出てくる再帰です。ここでは階乗(factorial)を実装します。
Python
Pythonだったら、こう書くでしょう。
#!/usr/bin/env python
from sys import argv
def factorial(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial(n-1)
if __name__ == '__main__':
n = int(argv[1])
print(factorial(n))
この再帰は、与えられたnから逆順に計算していきます。つまり、最初はn * factorial(n-1)、次にn-1 * factorial(n-2)と順に下がっていき、最後にnが1のときに1! = 1を返します。教科書通りです。
n=20のときの実行結果は次の通りです。
$ ./factorial.py 20
2432902008176640000
jq
jqも同じようにコーディングするだけです。ただ、Pythonでは関数引数だったnは入力.で扱います。関数呼び出しをしなければならないので、関数で定義します。
def factorial:
tonumber |
if . == 0 or . == 1 then
.
else
. * (. - 1| factorial)
end;
elseブロックでは、n-1を関数に入力するのに、. - 1 | factorialとパイプを使わなければならないので、Pythonよりは読みにくくなっています。でも、構成は同じです。
関数定義は宣言のdefに続いて関数名、シグニチャを区切るコロン:で始まり、以下コードブロックです。関数末尾(ここではend)にはセミコロン;で終了を明示的に示します。
関数を用意したファイルをfactorial.jqとして、これを読み込むにはinclude "factorial";をフィルタに記述します。ファイル拡張子はjqであることが期待されており、インクルードするときにはこれを外します。ここも、セミコロンで終端します。
n=20のときの実行結果は次の通りです。
$ jq -n 'include "factorial"; 20 | factorial'
2432902008176640000
同じ結果が得られました。即値の20より前にincludeを書くのがポイントです。
おわりに
最初は半信半疑でしたが、再帰も、やればできるもんです。すごいですね、jq。
参考
-
./jq -
jqのオフィシャルサイトです(英文)。 -
Qiita jq tag - Qiita掲載の
jq関連の記事一覧です。 -
Hello World あたたたた 24日目 jq編 -
jqの言語仕様をていねいに説明してくれます。 - jqハンドブックーNetOps/DevOps必携のJSONパーザ - 関数定義はこの本の10.4節で軽く紹介されています。ご購入はこちらから【出版社 | honto | amazon.co.jp | ヨドバシカメラ】。
