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
関連の記事一覧です。 - jqハンドブックーNetOps/DevOps必携のJSONパーザ - 関数定義はこの本の10.4節で軽く紹介されています。ご購入はこちらから【出版社 | honto | amazon.co.jp | ヨドバシカメラ】。