簡単に分かる悪い設計と良い設計

最近自分が悪い設計のコードを苦しみながら書いて、書いたあとに考えたら悪い設計をしていたことに気づいたので、今回自戒のために書いた。

自分自身プログラミングの技術が高いわけじゃないけどそれでも書いたあとにこれはマズかったと分かる例の話をする。

細かいコードの書き方に話が飛ぶとダルすぎるのであくまで文章だけで書く。

 

 

例:テキストファイルが10ファイルあって10ファイル全てのテキストファイルを合わせた合計の文字数を数えたい

注:それぞれのテキストファイルのファイルサイズが巨大で10TBを超えるとか10ファイルが時間差で揃うから逐次的に処理しなければいけないとかそういう込み入った前提ではないとする

 

 

明らかに悪い設計:

1ファイルずつ開いて1行毎の文字数をカウントして1ファイル毎に変数を1つずつ作って変数に保持する。最後に10個の変数を合計する。

普通と思われがちだが悪い設計:

1ファイルずつ開いて1ファイル毎の文字数を1つの変数に足していく。それを10ファイル全てに対して繰り返す。

いちばんマシな設計:

10ファイル全てを結合して1つのファイルにする。その結合してできたファイルを1ファイル全ての文字数を数える関数で文字数をカウントする(変数を使わず繰り返しも行わない)。

 

いちばんマシな設計の例をシェルスクリプトで書いた(改行込みならtrコマンド部分を除いてコマンド2つで書ける)

 

別のアナロジーを試したので書く。

目的:野菜スープを作りたい

悪い例:ニンジン畑に行ってニンジンを1つ掘り返し、その場で掘り返したニンジンを洗って切って、鍋に入れて、入れたニンジンの量に比例する水を鍋に足す。→ジャガイモ畑に行ってジャガイモを1つ掘り返し、その場で掘り返したジャガイモを洗って切って、鍋に入れて、入れたジャガイモの量に比例する水を鍋に足す。→玉ねぎ畑に行って玉ねぎを1つ掘り返し、その場で掘り返した玉ねぎを洗って切って、鍋に入れて、入れた玉ねぎの量に比例する水を鍋に足す。→他の野菜も同様に繰り返す→鍋を火にかけて煮る。→もう一人分の鍋が必要なので鍋を用意する(驚くべきことに先程の鍋は一人分だったのだ)→ニンジン畑に行って.......→鍋は10人分必要なので合計10回繰り返す。

→調理人はあまりの手際の悪さにキレて帰る。

 

 

良い例:ニンジン畑に行って10人分のニンジンを掘る。他のジャガイモと玉ねぎも同様に掘る。全部の野菜を1箇所に集める→全て洗う→全て切る→10人分の鍋に入れる→10人分の水を入れる→鍋を火にかける。

→どうにか鍋は1人分と同じ手順で作れて調理人も満足。

 

 

つまり何が言いたいかというと、作業工程を混合してそれを何度も繰り返すというのは複雑で理解が難しく(ソースコードを書きづらく読みづらい)、作業工程を混ぜず繰り返しをしないほうが完結で理解が簡単(ソースコードが書きやすく読みやすい)。

抽出するやつをlispでやるかから今現在までの流れ

Common Lispやってたんだけどlispよりシェルスクリプトが面白くなってきて最近ずっとシェルスクリプトやってた

前回

lisp-yanaka.hatenablog.com

抽出するやつをlispでやるかから今現在までの流れ

流れとしては

railsの会社入るしruby on railsやるか→これは実際けっこうダルい→firebaseでアプリ作るか→JS必要やん→JSやる→少しだけ書けるようになってnode.jsでクローラー書いてエッチなサイトからURLとそのURLのレートを拾ってembeddedで複数のサイトの動画を見れるサイトをfirebaseで作る→underscore.jsのコード読んだりする→むずい→JS書きづらすぎて飽きる→技術広げたい→エッチな動画落とすのにcurlでやるプログラムをpython経由でいじる→直接シェルスクリプト書く→コマンドをパイプで繋げて処理書くの実質関数型プログラミングやん!!→shで処理書くのめっちゃ簡単→()もいらない;もいらない変数もいらない→シェルスクリプトでやれること広げたい→正規表現を改めて学習→kindle to calibreめっちゃ便利→紙の本だとコピペ試せない→OCRや→google vision apiOCR試す→OCRめっちゃ簡単やん→時代は機械学習か→awkを学習→なんやこれめっちゃ便利やんけ→コマンドラインツール入れたり情報収集したり→sublimetextとウィンドウ切り替えしながらスクリプト書くのダルい→vimやってみるか→vim結構覚えるのむずい→機械学習やってみるか→python覚える必要あるな→『ベイズ推論による機械学習入門』を読む→冒頭で線形代数微分積分を覚えてる前提で話が進む→それはそうだな→線形代数のテキスト集めて覚え始める→飽きる(そのうちリトライ)→画像分類なら比較的簡単っぽいし機械学習体験版ができる(コピペだけでいけそう)→シェルスクリプトでクローラ書いて画像大量に集めて飽きる→改めてシェルスクリプト強力だと実感する→文章処理の機械学習ってどうなんだろ→fasttext試す→めっちゃ簡単な上に強力→calibった洋書をmecabしたりして頻度順にソートしてfasttextしたりする→良い感じ→unabridgedのオーディオブック聞くの再開(続いている)→couseraのML学習が1週間以内なら無料との情報をキャッチ→始める→手を動かすのが少ないから飽きる→python始めるまえにpythonの情報集めるやつ作るか→シェルスクリプトtwitterapirubyのgemで動かす→limit低すぎ→puppeteerでやるか→チャットボットの記事読む→dialogflow面白そうやんけ→いまここ

どんだけ飽き性なんだよって話だけどそれなりに技術が身についてる部分もあって一概に全部中途半端とも言えない感じ

道具箱の中身を充実させててその証拠にコマンドラインだけでhtml編集してfirebaseにhtmlアップしてデプロイするとかできるようになった

技術への考え方がコマンドラインとコマンドみたいなウェブサイトとかプログラミング言語とそのライブラリをAPIとして捉えるようになってきてデータを左から右に透過させてインターフェイスを作るみたいな考え方を最近してて実際楽しい

gistにプログラムどんどん放り込んでる

https://gist.github.com/taroyanaka

Common lispならセットアップから実行まで13行でスクレイピング可能

おい、ヲタクのお前らlispに興味あるか?Webには?

無いって?

知ったことか

じゃあ始めよう

 

1.SBCLをインストールしよう。SBCLが何かって?ググってくれ

$ brew update
$ brew install sbcl
clispっていう処理系はアップデート止まってるから使わなくてよし

 

2.quick lispをインストール。quick lispとは?ググれ

$ curl -O http://beta.quicklisp.org/quicklisp.lisp
$ sbcl --load quicklisp.lisp

 

こっからはsbclのコンソールの操作だ

(quicklisp-quickstart:install)
(ql:add-to-init-file)

3.quick lisplispのライブラリをインストール
(ql:quickload :drakma)
(ql:quickload :plump)
(ql:quickload :clss)

4.コーヒーでも飲んで一息ついてくれ

5.以下コピペ
(defvar html (drakma:http-request "http://www.paulgraham.com/hp.html"))
(defvar parse-html (plump:parse html))
(plump:text (car (coerce (clss:select "title" parse-html) 'list)))

 

以上で「"Hackers and Painters"」と表示されたら問題ない

※ちなみに環境はmacOS 10.12

これはdev.toで書いた記事でリアクションは7つ付いた

lispやるならdev.to(英語圏)だね

Begginnig Common Lisp for web scraping

 

 

hey nerds, do you like web? interest lisp?
no?
i dont care
okay i do

Step1: install sbcl
$ brew update
$ brew install sbcl
(now, clisp is stopped update)

Step2: install quick lisp
$ curl -O http://beta.quicklisp.org/quicklisp.lisp
$ sbcl --load quicklisp.lisp

----------↓↓↓starting sbcl console↓↓↓-----

(quicklisp-quickstart:install)
(ql:add-to-init-file)

Step3: install libraries to sbcl by quicklisp
(ql:quickload :drakma)
(ql:quickload :plump)
(ql:quickload :clss)

Step4: drink a coffee

Step5: write codes and eval
(defvar html (drakma:http-request "http://www.paulgraham.com/hp.html"))
(defvar parse-html (plump:parse html))
(plump:text (car (coerce (clss:select "title" parse-html) 'list)))

on lispを手に取ってから今現在までの流れ

ここまでの流れ

だいぶ前に買って持ってたon lisp読む→全然意味が分からない→けど面白そう→楽器やるの飽きた時にlispやるか→lisp全然分かんねー→実践common lispが良いらしいので借りて読む→全然分かんねー→入門 common lisp借りて読む→ちょっとだけ分かる→って言っても作りたいものないとモチベーション続かない→webサイト作りたい→参考にするプログラムないだろうか→ウェブフレームワークって言ったらruby on railsrubyそんな詳しくない→楽しいrubyのプログラムリスト読む→試しにプログラムリストのページのrubyプログラム部分だけ抽出rubyプログラム書くか→抽出できた→lisp on railsやりたいならrubyのコード読む環境整えなきゃな→ ctags導入→sublime text良さそう→sublime textとsublime textのctagsプラグイン導入→これめっちゃ良いやんけ→sublime textのlisp環境作れるやんけ→sublime textでreplめっちゃ便利!!→on lispの隣にシェルスクリプトのリファレンス本あったので読む→シェルスクリプトめっちゃ便利やんけls >> list.txtとか→前作ったrubyプログラム部分抜き出す奴をシェルスクリプトで作る→できない...→教えてgooに質問投稿→3時間くらいで回答あった→教えてgooめっちゃ便利やんけ→人間は1人では生きていけない→学びがある→入門common lispにあるコードとかマンガで学ぶlispのコードをぼちぼち覚えるか→やっぱりrubyでできることをlispで再現するか→抽出するやつをlispでやるか→イマココ

lisp chords 2

'lico

'suzu

(cons 'lico 'suzu)

'1

(cons 2 nil)

(cons 1 (cons 2 nil))

(cons '+ (cons 1 (cons 2 nil)))

(cons 1 (cons 2 nil))

(eval '(+ 1 2))

 

(eval '(cons '+ (cons 1 (cons 2 nil))))

(eval '(list '+ 1 2))

 

 

'(cons '+  (cons 1 (cons 2 nil)))

 

 

(eval (eval '(cons '+ (cons 1 (cons 2 nil)))))

(eval (eval '(list '+ 1 2)))

 

(eval '(cons '+ (cons 1 (cons 2 nil))))

(eval (cons '+ (cons 1 (cons 2 nil))))

 

(cons 'a (cons 'b nil))

(cons 'a (cons b nil))

(cons 'a (cons "b" nil))

(cons a (cons "b" nil))

 

(cdr (cons 'a (cons 'b nil)))

(cdr '(a b))

(car '(quote a))

(car ''a)

lisp chords 1

f:id:lisp_yanaka:20170318151937j:plain

(+ 1 2 3)
(+ 1 (/ 1 3))
(+ 1/3 1/3 1/3)
(+ 0.3 0.3 0.3)

 

(cons 1 2)
(car (cons 1 2))
(cdr (cons 1 2))
(cons (cdr (cons 1 2))3)
(cdr (cons (cdr (cons 1 2)) 3))
(car (cons (cdr (cons 1 2)) 3))
(cons (cons 1 2) (cons 3 4))
(list (cons 1 2) (cons 3 4))
(car (cons (cons 1 2) (cons 3 4)))
(cdr (cons (cons 1 2) (cons 3 4)))
(cdr (cdr (cons (cons 1 2) (cons 3 4))))
(* (+ 1 2) (- 3 4) (+ 5 6))
(defun 2jyo (n) (* n n))
(2jyo 2)
(defun inc (x &optional (y 1)) (+ x y))
(inc 1)
(inc 1 2)
(+ (print (+ 1 2)) (print (+ 3 4)))
'(+ 1 2)