SlideShare a Scribd company logo
One
Common Lispでもワンライナーしたい
自己紹介
● twitter: @sin_clav
● github: @t-sin
● 野生のCommon Lisp使い
● おしごとはnil
今日お話しすること
● Common Lispでワンライナー
やったらツラかった(動機)
● ワンライナーを楽にしてやるぜ(結果)
● どうワンライナーするのか(使い方)
● その内部構造(設計・実装)
シェルのワンライナー
よくお世話になりますよね
よくやるワンライナー(1)
● アクセスログ中のhoge APIへのアクセス
● そのログ中のhoge APIのアクセス数
$ cat /var/log/nginx/access.log | grep /api/hoge
xx.xx.xx.xx - - [21/Oct/…] "GET / HTTP/1.1" ….
xx.xx.xx.xx - - [22/Oct/…] "GET / HTTP/1.1" ….
xx.xx.xx.xx - - [24/Oct/…] "GET / HTTP/1.1" ….
$ cat /var/log/nginx/access.log | grep /api/hoge |
wc -l
● 3
よくやるワンライナー(2)
● CSVファイル中の2列目の合計
$ cat data.csv
id1,1
id2,2
id3,3
$ cat data.csv | awk -F , '{sum+=$2}END{print sum}'
6
よくやるワンライナー(2)
● CSVファイル中の2列目の合計
shell以外の言語(awk)おぼえないとダメ…?
$ cat data.csv
id1,1
id2,2
id3,3
$ cat data.csv | awk -F , '{sum+=$2}END{print sum}'
6
Common Lispでやってみる
● CSVファイル中の2列目の合計
● ぜんぶCommon Lispで。
$ cut -d ',' -f 2 data.csv | ros run -e '(print
(loop for line = (read *standard-input* nil :eof)
until (eq :eof line) sum line))' -q
6
$ ros -s split-sequence -e '(with-open-file (in
"data.csv") (print (loop for l = (read-line in
nil :eof) until (eq l :eof) sum (parse-integer (nth
1 (split-sequence:split-sequence #, l))))))' -q
● 6
ながすぎてしぬ
われわれはどうすればいいのだ……
ワンライナーっぽさのために
● 入力まわりのタイプ数を減らす
– *standard-input*
– with-open-fileうんぬん
– 行に対してのloop
● 「処理」の合成っぽく書けるとよい
ワンライナーを支援するワン!
● CSVの2列目合計をoneで。
● ぜんぶoneで。
$ cut -d ',' -f 2 data.csv | ros one '(one:for* - <
one:read* +> + 0)'
6
$ ros one '(one:for* #P"data.csv" < one:read-line*
$ #/(split-sequence #, _) $ #/(nth 1 _) $ parse-
integer +> + 0)'
6
Oneとは
「処理」を
連結して
入力をwrapする
ライブラリです!!
Oneの機能概要
ゆるいお気持ちでお聞ききください
Oneの基本構文
● 前段の入力を、次の記号で受ける
CL-USER> (one:for 入力 [記号 パラメータ]*)
Oneの基本構文
● 前段の入力を、次の記号で受ける
CL-USER> (one:for 入力 [記号 パラメータ]*)
オブジェクト,
stream,
pathname
パイプ的な記号
$, ?, <, >, +>
1引数関数,
シンボル
Oneの基本構文
● 前段の入力を、次の記号で受ける
● 1引数lmbda式用リーダマクロ
CL-USER> (one:for 入力 [記号 パラメータ]*)
オブジェクト,
stream,
pathname
パイプ的な記号
$, ?, <, >, +>
1引数関数,
シンボル
;; (lambda (input) (search "hoge" input)) と同じ
#/(search "hoge" _)
入力
記号`$`: 処理の合成
● 前段の入力にパラメータ(処理)を合成
● 例
CL-USER> (one:for 入力 $ 関数or関数名 ...)
CL-USER> (one:for 1 $ print)
"1" ; 入力にprintが適用された
CL-USER> (one:for 1 $ 1+ $ print)
"2" ; 1+の後にprintが適用された
記号`<`: 入力の上を繰り返し
● 前段の入力の上をloopする(関数で)
● 例
CL-USER> (one:for 入力 < 関数or関数名 ...)
CL-USER> (one:for '(1 2 3) < cdr $ princ)
123 ; cdrでloopしたものが出力された
CL-USER> (one:for #P"nums.csv" < one:read-line* $
print)
"id1,1"
"id2,2" ; read-line* (:EOFで終わる) でloopした
記号`?`: 条件によるフィルタ
● 前段の入力のうち述語でtになるもののみ通す
● 例
CL-USER> (one:for 入力 ? 述語 ...)
● CL-USER> (one:for '(1 2 3) < cdr ? oddp $ print)
1
3 ; 奇数だけが出力された
記号`>`: 処理結果を溜め込む
● 前段の入力をリストに溜め込んで処理する
● 例
CL-USER> (one:for 入力 > 変換する関数 ...)
CL-USER> (one:for 1 > identity $ print)
(1) ; 入力がリストになる
CL-USER> (one:for '(1 2 3) < cdr > identity $
print)
(1 2 3) ; 前段の入力すべてがリストになる
CL-USER> (one:for '(1 2 3) < cdr > #/(apply #'+ _)
$ print)
6 ; 合計された
記号`+>`: 処理結果を畳み込む
● 前段の入力をバッファせずに畳み込む
● 例
CL-USER> (one:for 入力 +> 2引数関数 [初期値] ...)
CL-USER> (one:for '(1 2 3) < cdr +> (lambda (a b)
(+ a b)) 0 $ print)
6 ; 足し込まれた
; 長い。lambdaの部分が+だけで書けるといいかも
実際の使用例
ここからが本当にやりたかったことです
ワンライナーを支援するワン!
● CSVの2列目合計
● 標準入力をソート
$ seq 1 5 | shuf | ros one "(one:for* - < one:read-
line* > #/(sort _ #'string<))"
1
…
5
$ ros one '(one:for* #P"data.csv" < one:read-line*
$ #/(split-sequence #, _) $ #/(nth 1 _) $ parse-
integer +> + 0)'
6
シェル芸(1): 響け!ユーフォニアム
『【ファン迷惑】「響け!ユーフォニアム」という文字列だけで遊
ぶシェル芸人達』, https://togetter.com/li/1041621
$ echo 響け!ユーフォニアム | ros one '(one:for - <
one:read-line* $ #/(cons _ (length _)) $ #/(cons
(repeat-sequence (car _) (1+ (cdr _))) (cdr _)) $ #/
(batches (car _) (1+ (cdr _))) < cdr $ #/(format t "~a~
%" _))'
響け!ユーフォニアム響
け!ユーフォニアム響け
!ユーフォニアム響け!
ユーフォニアム響け!ユ
ーフォニアム響け!ユー
フォニアム響け!ユーフ
ォニアム響け!ユーフォ
ニアム響け!ユーフォニ
アム響け!ユーフォニア
ム響け!ユーフォニアム
シェル芸(2): サンシャイン池崎ゲーム
https://twitter.com/ziuziu/status/918070729341587457
$ echo いまはもううごかないおじいさんのとけい | ros one
'(one:for* - < one:read-line* $ #/(ppcre:regex-replace-
all "い" _ "イェー!"))'
イェー!まはもううごかなイェー!おじイェー!さんのとけ
イェー!
Oneの中身は?
(ちなみにここまででスライド26枚です)
設計のポイント
● 部品となる「処理」を独立させる
● 処理間を渡るデータはLispオブジェクト
● 省タイプ化
– 入力に対する制御構文(loopやif)を隠す
– 入力の煩雑な処理(長い名前等)を隠す
● 省メモリ
パイプを見直す
$ cat access.log | grep html | sed 日時 | sort
パイプを見直す
$ cat access.log | grep html | sed 日時 | sort
grep sedcat sort
パイプを見直す
$ cat access.log | grep html | sed 日時 | sort
grep sedcat sort
grep sedcat sort( )) ) )(((
パイプを見直す
$ cat access.log | grep html | sed 日時 | sort
grep sedcat sort
grep sedcat sort( )) ) )(((
(sort (sed (grep (cat "access.log"))))
パイプを見直す
$ cat access.log | grep html | sed 日時 | sort
grep sedcat sort
grep sedcat sort( )) ) )(((
(sort (sed (grep (cat "access.log"))))
関数合成だ!
〜パイプからLispへ〜
省メモリを考える
● 入力を全部メモリに置いてはダメ
● 入力はすぐ次の処理に流す
;; ダメなコード
;; (access.logが10GBあったら…?)
(sort (mapcar #'sed
(mapcar #'grep
(cat "access.log")))
#'string<)
(sort (loop
:for line in (cat "access.log")
:when (grep line)
:collect (sed line))
#'string<)
処理フローの組み立て(1)
● 内部でこんなコールグラフをもつ、いっこの
lambda式をつくりたい
cat
grep
grep
grep
grep
sed
sed
sed
sort
< ? >
: 入力の流れ
: 処理のlambda式
処理フローの組み立て(2)
● lambda式を連ねてフローを作る
– こんな関数でパーツを…
– 連ねる
;;`$`はだいたいこんな感じ
(defun compose (fn2 fn1)
(lambda (input)
(funcall fn2 (funcall fn1 input)))
;; cat | grep | sed
(compose (compose #'grep #'sed) #'cat)
感想
● lambda式の入れ子地獄はデバッグつらい
● シェルやREPLで、まあまあ使える
● まだまだ改善の余地ありそう
– `+>`に2引数lambda式を書くところなど
Oneをためしに使ってみてね。
`$ ros install t-sin/one`で入るよ!

More Related Content

PDF
Inquisitor -Common Lispに文字コード判定を-
PDF
Sounds Like Common Lisp - ゼロからはじめるサウンドプログラミング
PDF
謎の言語Forthが謎なので実装した
PDF
Lisp tutorial for Pythonista : Day 1
PDF
Rpn and forth 超入門
PDF
LLdeade Python Language Update
PDF
続・SECDマシン
KEY
ひのきのぼうだけで全クリ目指す
Inquisitor -Common Lispに文字コード判定を-
Sounds Like Common Lisp - ゼロからはじめるサウンドプログラミング
謎の言語Forthが謎なので実装した
Lisp tutorial for Pythonista : Day 1
Rpn and forth 超入門
LLdeade Python Language Update
続・SECDマシン
ひのきのぼうだけで全クリ目指す

What's hot (18)

PDF
Boost tour 1.60.0 merge
PDF
pecoを使おう
PDF
すごい constexpr たのしくレイトレ!
PDF
Boost Tour 1.48.0 diff
PDF
Constexpr 中3女子テクニック
PDF
Python 機械学習プログラミング データ分析ライブラリー解説編
PDF
SECDマシン 実装と動きとその他もろもろについて
PDF
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
PDF
Write good parser in perl
PDF
Scapy presentation Remake(訂正)
PDF
R3.0.0 is relased
PDF
Rでreproducible research
PDF
Scapy presentation
PPT
Glibc malloc internal
PDF
Find(1)
PDF
GNU awk (gawk) を用いた Apache ログ解析方法
PPTX
Node.js - sleep sort algorithm
PDF
メタメタプログラミングRuby
Boost tour 1.60.0 merge
pecoを使おう
すごい constexpr たのしくレイトレ!
Boost Tour 1.48.0 diff
Constexpr 中3女子テクニック
Python 機械学習プログラミング データ分析ライブラリー解説編
SECDマシン 実装と動きとその他もろもろについて
constexpr関数はコンパイル時処理。これはいい。実行時が霞んで見える。cpuの嬌声が聞こえてきそうだ
Write good parser in perl
Scapy presentation Remake(訂正)
R3.0.0 is relased
Rでreproducible research
Scapy presentation
Glibc malloc internal
Find(1)
GNU awk (gawk) を用いた Apache ログ解析方法
Node.js - sleep sort algorithm
メタメタプログラミングRuby
Ad

Similar to One - Common Lispでもワンライナーしたい (20)

PPTX
Boost jp9 program_options
PDF
Ilstudy001_20110806
PDF
これからの「言語」の話をしよう ―― 未来を生きるためのツール
PDF
C++コミュニティーの中心でC++をDISる
PDF
Common LispでGPGPU
PPT
ZFSのソースコードをチラ見してみる
PDF
Tokyor23 doradora09
ODP
0x300
PDF
2017-12-04 Linuxの基本構造とBashでの扱い方
PDF
Cli mini Hack!#1 ~Terminalとの親睦を深めよう~
PDF
つくっておぼえる!仮想マシン〜直前で実装編〜
PDF
シェル芸初心者によるシェル芸入門
PDF
OCamlのアセンブラを読む話
PDF
10min r study_tokyor25
PDF
10min r study_tokyor25
PDF
Java SE 8 lambdaで変わる プログラミングスタイル
PPTX
そんな装備で大丈夫か?
PDF
Application Developer Festival 2015 LT
PDF
Lisp Tutorial for Pythonista : Day 3
Boost jp9 program_options
Ilstudy001_20110806
これからの「言語」の話をしよう ―― 未来を生きるためのツール
C++コミュニティーの中心でC++をDISる
Common LispでGPGPU
ZFSのソースコードをチラ見してみる
Tokyor23 doradora09
0x300
2017-12-04 Linuxの基本構造とBashでの扱い方
Cli mini Hack!#1 ~Terminalとの親睦を深めよう~
つくっておぼえる!仮想マシン〜直前で実装編〜
シェル芸初心者によるシェル芸入門
OCamlのアセンブラを読む話
10min r study_tokyor25
10min r study_tokyor25
Java SE 8 lambdaで変わる プログラミングスタイル
そんな装備で大丈夫か?
Application Developer Festival 2015 LT
Lisp Tutorial for Pythonista : Day 3
Ad

One - Common Lispでもワンライナーしたい