Skip to content

Instantly share code, notes, and snippets.

@egisatoshi
Last active January 1, 2016 07:24
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save egisatoshi/af505ea38ad2fa7218e4 to your computer and use it in GitHub Desktop.
Egisonコマンドラインチュートリアル

Egisonコマンドラインチュートリアル

式を評価

--evalまたは-eオプションで引数の式を評価できます。 式はシングルクォート(`)で囲みます。

$ egison -e '(take 10 primes)'
{2 3 5 7 11 13 17 19 23 29}

TSVを出力

--tsvまたは-Tオプションにより、出力の形式がTSVになります。

評価結果のコレクションの要素のそれぞれが1行ごとに出力されます。

$ egison -T -e '(take 10 primes)'
2
3
5
7
11
13
17
19
23
29

内側の要素がコレクションである場合、それぞれの要素がタブで区切られます。

$ egison -T -e '(map p-f (between 100 110))'
2	2	5	5
101
2	3	17
103
2	2	2	13
3	5	7
2	53
107
2	2	3	3	3
109
2	5	11

内側の要素がタプルである場合にも、それぞれの要素がタブで区切られます。

$ egison -T -e '(zip nats primes)' | head -n 10
1	2
2	3
3	5
4	7
5	11
6	13
7	17
8	19
9	23
10	29

TSVを入力として受け取る

以下説明する--map--filter--substituteオプションは入力のTSVをパースして、デフォルトでは、各行の値はタプルとして読み込みます。

入力を行毎に操作(--map オプション)

--mapまたは-mオプションにより入力の各行毎に処理を行うことができます。

$ seq 100 110 | egison -T -m '(lambda [$x] [x (p-f x)])'
100	2	2	5	5
101	101
102	2	3	17
103	103
104	2	2	2	13
105	3	5	7
106	2	53
107	107
108	2	2	3	3	3
109	109
110	2	5	11

入力を行毎に絞り込み(--filter オプション)

--filterまたは-fオプションにより入力の各行毎にユーザ指定の条件による絞り込みを行うことができます。

デフォルトでは各行の値はタプルとして読み込まれます。

$ seq 1 20 | egison -f 'prime?'
2
3
5
7
11
13
17
19

入力をまるごと無限ストリームとして操作 (--substitute オプション)

--substituteまたは-sオプションにより入力をまるごと無限ストリームとして操作できます。

デモンストレーションとして双子素数をシェル上で出力してみます。

シェル上で双子素数を列挙

以下のようにシェル上で双子素数を出力できます。

$ seq 1 100 | egison -f 'prime?' | egison -T -s '(match-all-lambda (list integer) [<join _ <cons $p <cons ,(+ p 2) _>>> [p (+ p 2)]])'
3	5
5	7
11	13
17	19
29	31
41	43
59	61
71	73

以下のように1つのEgison式で双子素数を出力することも出来ます。

Egisonコマンドの出力については、パイプで渡された先で必要なだけしか評価されないようになっています。

$ egison -T -e '(match-all primes (list integer) [<join _ <cons $p <cons ,(+ p 2) _>>> [p (+ p 2)]])' | head -n 8
3	5
5	7
11	13
17	19
29	31
41	43
59	61
71	73

Awkの場合

双子素数の場合はawkでも短い記述で取得することができます。

$ seq 2 100 | factor | awk 'BEGIN{p=2}(NF == 2){if (p + 2 == $2){print p "\t" $2;};p=$2}'
3	5
5	7
11	13
17	19
29	31
41	43
59	61
71	73

ただし、(p,p+6)の組み合わせのようなより一般的な組み合わせの列挙に簡単に対応するのはegisonコマンドを使わないと難しいです。

より高度なオプション

--fieldまたは-Fオプションにより、TSVのパースの方法を指定することが出来ます。

下記の用に-F 2cと引数に与えると2列目以降をコレクションとしてまとめて扱うことが出来ます。

このオプションはsortコマンドの-kオプションを参考にデザインされています。 例えば、-F 2,4cとすると2列目から4列目を{}で囲みコレクションとして扱います。

$ seq 10 20 | egison -T -m '(lambda [$x] [x (p-f x)])' | egison -F 2c -m 'id'
[10 {2 5}]
[11 {11}]
[12 {2 2 3}]
[13 {13}]
[14 {2 7}]
[15 {3 5}]
[16 {2 2 2 2}]
[17 {17}]
[18 {2 3 3}]
[19 {19}]
[20 {2 2 5}]

c以外にsもあります。 例えば、以下のように-F 1,1sというようにオプションを指定すると1列目の要素を"により囲み文字列としてパースできるようになります。

また、例えば、-F 1,3sとオプションを指定したとすると、1列目から3列目までの要素がそれぞれ"で囲まれます。

$ seq 10 20 | egison -T -m '(lambda [$x] [x (p-f x)])' | egison -F 1,1s -F 2c -m 'id'
["10" {2 5}]
["11" {11}]
["12" {2 2 3}]
["13" {13}]
["14" {2 7}]
["15" {3 5}]
["16" {2 2 2 2}]
["17" {17}]
["18" {2 3 3}]
["19" {19}]
["20" {2 2 5}]

CSVを処理する

このチュートリアルの最後に、実データの解析をコマンドラインでEgisonで行うデモをします。 世界の各国の平均寿命の推移を解析してみます。

今回使用するこの平均寿命の元データはWorld BankのWebサイトから取得しました。 今回使用するデータは、CSVファイルになっており、ここからダウンロードできます。

CountryName,1960,1961,1962,1963,1964,1965,1966,1967,1968,1969,1970,1971,1972,1973,1974,1975,1976,1977,1978,1979,1980,1981,1982,1983,1984,1985,1986,1987,1988,1989,1990,1991,1992,1993,1994,1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009
Afghanistan,31.13209756,31.47529268,31.83202439,32.20426829,32.59104878,32.98987805,33.39673171,33.80914634,34.22307317,34.6375122,35.05597561,35.48141463,35.91687805,36.36131707,36.81073171,37.25712195,37.69097561,38.10629268,38.49658537,38.86185366,39.20412195,39.52695122,39.83982927,40.1492439,40.45868293,40.77058537,41.08487805,41.399,41.71092683,42.02119512,42.32936585,42.636,42.93973171,43.24209756,43.54265854,43.84090244,44.13680488,44.42980488,44.72134146,45.00985366,45.29285366,45.56726829,45.83314634,46.09243902,46.35119512,46.61641463,46.89909756,47.20531707,47.53856098,47.89885366
...

平均寿命が75歳を超えたことがある国だけ列挙

tailコマンドにより、先頭行をスキップします。 sedによりCSVファイルをTSVファイルを変換します。 Egisonのパターンマッチにより、対象の行のみを取得します。

$ tail -n +2 all.csv | sed -e 's/,/\t/g' | egison -T -F 1,1s -F 2c -f '(lambda [$c $ds] (match ds (list integer) {[<join _ <cons ?(gt? $ 75) _>> #t] [_ #f]}))' | sed -e 's/\t/,/g'
"Albania",62.25436585,63.27346341,64.16234146,64.88709756,65.43768293,65.82943902,66.10007317,66.31180488,66.51643902,66.73846341,66.98943902,67.2655122,67.54446341,67.8135122,68.07226829,68.32582927,68.57770732,68.83434146,69.09963415,69.37707317,69.67404878,70.00046341,70.3512439,70.71541463,71.0764878,71.39758537,71.63736585,71.77492683,71.80936585,71.75421951,71.64543902,71.5314878,71.46521951,71.487,71.61773171,71.87029268,72.24107317,72.69607317,73.19780488,73.72182927,74.23873171,74.72365854,75.16121951,75.54202439,75.85912195,76.112,76.30963415,76.47341463,76.6207561,76.76109756
...

平均寿命が5年以上下がったことがある国だけ列挙

パターン部の記述を変えるだけで、条件を色々変えた抽出が可能です。

$ tail -n +2 all.csv | sed -e 's/,/\t/g' | egison -T -F 1,1s -F 2c -f '(lambda [$c $ds] (match ds (list integer) {[<join _ <cons $x <join _ <cons ?(lt? $ (- x 5)) _>>>> #t] [_ #f]}))' | sed -e 's/\t/,/g'
"Bangladesh",47.75853659,48.23512195,48.80190244,49.41553659,49.98185366,50.22939024,49.8322439,48.66704878,46.78160976,44.36629268,41.88595122,39.93287805,38.96990244,39.25236585,40.7877561,43.33478049,46.44509756,49.53041463,52.12787805,54.04663415,55.23585366,55.80892683,56.08019512,56.30812195,56.58190244,56.95241463,57.42321951,57.93636585,58.44697561,58.95807317,59.47114634,59.98970732,60.51473171,61.04673171,61.58321951,62.12268293,62.66212195,63.19656098,63.72197561,64.23390244,64.73034146,65.20831707,65.66831707,66.10985366,66.53136585,66.93229268,67.31007317,67.66665854,68.00402439,68.32521951
...

カレントディレクトリ以下のファイル全てについて、同じパターンが連続する行に現れる部分を抽出する

$ grep -n -R "let " . | sed -e 's/:/\t/g' | cut -f1,2 | egison -T -F 1,1s -s '(match-all-lambda (list [string integer]) [<join _ <cons [$f $n] <cons [,f ,(+ n 1)] _>>> [f n (+ n 1)]])'
"lib/core/order.egi"	57	58
"lib/core/order.egi"	58	59
"lib/core/random.egi"	15	16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment