アメリカのペーパーバックはなんか軽い

\lambda
みんな大好き

収入が得られたので新しい本を買いました.

計算機プログラムの構造と解釈[第2版]

計算機プログラムの構造と解釈[第2版]

定番の魔術師本 訳の良し悪しについてしばしば議論を見かけるけれど今のところは読めてる.

計算論 計算可能性とラムダ計算 (コンピュータサイエンス大学講座)

計算論 計算可能性とラムダ計算 (コンピュータサイエンス大学講座)

関数型プログラミングの根っこが知りたくて買った計算可能な関数やLambda算法に関する本

ANSI Common LISP (Prentice Hall Series in Artificial Intelligence)

ANSI Common LISP (Prentice Hall Series in Artificial Intelligence)

Lisp本の著者として有名なPaul GrahamさんのLisp

この三冊やるのに半年くらい掛かりそう…

OAuthのリクエストトークンがやっと取れました.

参考にしたページ

http://yuroyoro.hatenablog.com/entry/20100506/1273137673

http://d.hatena.ne.jp/tor_ozaki/20100530/1275166645

(require 'ironclad)    ;暗号化関係
(require 'cl-base64)   ;Base64
(require 'drakma)      ;HTTPリクエスト投げる

(defparameter *key* "foooooooooooooo")
(defparameter *secret* "baaaaaaaaaaaaaaar")
(defparameter *acc-token* nil)
(defparameter *acc-secret* nil)
(defparameter *request-token-uri* "https://api.twitter.com/oauth/request_token") 

(defun make-par ()
  (list
   `("oauth_consumer_key" . ,*key*)
   `("oauth_nonce" . ,(nonce-gen))
   `("oauth_signature_method" . "HMAC-SHA1")
   `("oauth_timestamp" . ,(format nil "~a"(get-unix-time)))
   `("oauth_token" . ,*acc-token*)
   `("oauth_version" . "1.0")))

(defun get-unix-time ()
  (- (get-universal-time) 2208988800))  ;70年早い

(defun nonce-gen ()
  (let ((str "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"))
    (concatenate 'string
         (loop for n to 32
            collect (char str (random 62))))))

(defun hmac-base-gen (method url params)
  (let* ((lst (sort (loop for n in params
              collect (format nil "&~a=~a" (car n) (if (cdr n)
                                   (cdr n)
                                   "")))
           #'string-lessp))
     (str (list method "&" (perenc url) "&" (perenc (format nil "~a" (subseq (car lst) 1)))(perenc (format nil "~{~a~}" (cdr lst)))))) ;ここでハッシュ化する文字列の一部を余分にパーセントエンコードしてしまってハマった
    (format nil "~{~a~}" (loop for s in str collect s))))
  
(defun perenc (str) ;後でちゃんとやります
  (format nil "~{~a~}" (loop for ch across str for index from 0
              collect (cond ((eq ch #\:) "%3A")
                    ((eq ch #\/) "%2F")
                    ((eq ch #\&) "%26")
                    ((eq ch #\=) "%3D")
                    ((eq ch #\+) "%2B")
                    (t ch)))))

(defun hmac-sha1 (key str)
  (let* ((str (string-to-octets str))
     (key (string-to-octets key))
     (hmac (ironclad:make-hmac key 'ironclad:sha1)))
    (ironclad:update-hmac hmac str)
    (ironclad:hmac-digest hmac)))

(defun make-signature (meth url par)
  (cl-base64:usb8-array-to-base64-string 
      (hmac-sha1 (make-key) (hmac-base-gen meth url par))))

(defun make-key ()
  (let ((lst (list *secret* "&" *acc-secret*)))
    (format nil "~{~a~}" (loop for n in lst
                collect (format nil "~a" (if n
                             n
                             ""))))))

(defun make-header (meth url)
  (let* ((par (make-par))
     (par (append (list (car par) (cadr par) (cons "oauth_signature" (perenc (make-signature meth url par)))) (cddr par))))
    (with-output-to-string (strm)
      (princ "OAuth " strm)
      (format strm "~a=~s" (caar par) (cdar par))
      (mapc (lambda (x)
          (format strm ", ~a=~s" (car x) (if (cdr x)
                           (cdr x)
                           "")))
        (cdr par)))))

(setf drakma:*drakma-default-external-format* :utf-8)
(pushnew (cons "application" "xml") drakma:*text-content-types* :test 'equal)

(defun get-token ()
  (format nil "~a"
      (drakma:http-request *request-token-uri*
                   :method :post
                   :additional-headers `(,(cons "Authorization" (make-header "POST" *request-token-uri*))))))

これでget-tokenを呼び出すとoauth-tokenとoauth-secretが帰ってくるから,oauth-tokenをパラメータとしてブラウザで認証ページを開くと見慣れたページが出てくる.

f:id:sek4i:20140830150730p:plain

subseqとformatを使った文字列操作に持っていったのでもっとやりようはありそうな気がしてる.パーセントエンコードする関数はとりあえずここまででは問題ないけれど,ちゃんとした実装しないとTweetできない.

ここまでくればTweetできるまで後少し(多分)

Common Lisp で何かを作ってみる

何を作るのか

数時間何を作るのか考えてみたのだけれど,頻度の多寡こそあれ,高校の頃からずっとTwitterを利用し続けている自分としてはずっとTwitterクライアントを作ってみたかった.だけれどいざつくろうとしたことはなかった.今回Land of Lispで簡単なソケットを使ったコードを書いたのでなんだかできそうな気がしてきた.ということでTwitterクライアントを書いてみようと思う.

Twitterクライアントに必要そうなもの

f:id:sek4i:20140829153127p:plain

ということで,いざTwitterクライアントを作るとなると必要そうなものを現状挙げられるもので挙げてみた.APIのバージョンが変わった時にOAuthが必須になったというのを読んだことがあるため,具体的に何をしているのかは知らないが必要なのだろう.Authというくらいだから認証に利用するのだろう. 後はTweetしたりTLを持ってきたりといったAPIを使う機能があればいわゆるボットを作ることができそうな気がする. 人間が使うにはある程度インターフェースは整えなければいけないだろう.

方針

  • 車輪の再発明できそうなところは積極的に再発明していってどんな仕組みで動いているのか理解していく
  • 処理系は高速と噂のSBCL
  • Gitの使い方も覚えたい

当面の目標

とりあえず認証→Tweetまでできればいいなぁ……

Lispの国に行きたくて

Land of Lispを読了

Land of Lispをひと通り読んで掲載されたコードを動かしてみた後,ちょっとだけ簡単なコードで遊んでみた.中盤までは読み終わったら早速なにかつくろうと思っていたのだけれど後半の内容が結構重くて理解が難しかったところも結構あったため,結構な読了感でちょっとしたコードで遊ぶくらいの気持ちしかわかなかった,というのが正確だけど.

Land of Lisp

Land of Lisp

これから読む人は,Dice of Doomのゲーム木のフラクタルチックな構造が頭の中でヴィジュアルに思い浮かべられるようになるまで眺めれば,多分引っかからないで最後まで行けると思うよ?そこがクリアできればこの本で一番の難関は間違いなく開発環境を作るところ.CLISPを使うとしか書いてないけどCLISPだけじゃとてもじゃないけどコーディングできないって言う.開発環境に関してほとんど全くと行っていいほど説明がない以外は,教科書としてかなり秀逸な構成になってるように感じる.訳もかなり良かった.ハッカーと画家でも川合史朗さんの訳読んだけれど,どちらもリズムが良くてかつわかりやすい良訳.

遊びながら作ったガラクタ

ということで,なんの役にも立たないけどちょっとだけ本書で書かれてたことを反映したガラクタを紹介.

フィボナッチ数列のn番目に評価される関数

(let ((acc (make-hash-table)))
   (defun fib (n)
      (cond ((zerop n) 0)
            ((eq 1 n) 1)
            ((minusp n) nil)
            (t (or (gethash n acc) (setf (gethash n acc) (+ (fib (1- n)) (fib (- n 2)))))
               (gethash n acc)))))

クロージャを使ってハッシュテーブルを参照するので,loopマクロを使って順番に呼びだせばメモリが許す限りスタックオーバーフローを起こさずに計算します.処理系によってはGCがエラーを吐く模様.

> (loop for n to 200000
     do (fib n)
     if (eq n 200000) do (format t "~a" (fib n)))

//前略
574322186287559282059723208383808531011618825713740726934434742559093323706126196002712537368795203860187900060895217407977181945388053286947239052473752453296225053538805624817479197417467060417752514347515076498182239531088303954238221549351009970624402084900272950782638693852022231142741503929713071110792074068146485179081595572528904785663877616462653669462689359261747364212532866024476944192614519310076109974861926126352359630329214201957704677119789193142654798777312450935031089429802652246259408175853125

>

うちの環境だと200000まで計算するとメモリを1.6GBほど使います.二回目以降はハッシュテーブルから持ってくるだけだから処理が早いという利点はあるけど青天井にメモリを使ってしまいます.

さすがに頭が悪いのでアキュームレータを引数として取るローカル関数を定義して,アキュームレータにフィボナッチ数列の0番目と1番目の要素である0と1を渡して再帰呼出し.呼び出されるごとにnをデクリメントしてnが0の時のacc2の値がn番目の要素.

(defun fib (n)
  (labels ((f (n acc1 acc2)
             (cond ((minusp n) nil)
                   ((zerop n) acc2)
                   (t (f (1- n) (+ acc1 acc2) acc1)))))
     (f n 1 0)))
> (fib 200000)

//前略
574322186287559282059723208383808531011618825713740726934434742559093323706126196002712537368795203860187900060895217407977181945388053286947239052473752453296225053538805624817479197417467060417752514347515076498182239531088303954238221549351009970624402084900272950782638693852022231142741503929713071110792074068146485179081595572528904785663877616462653669462689359261747364212532866024476944192614519310076109974861926126352359630329214201957704677119789193142654798777312450935031089429802652246259408175853125

>

CLISPだと(compile 'fib)して末尾呼び出し最適化しないとダメでした.sbclならそのまま行けます.

非負整数の引数の階乗に評価される関数

(defun factorial (x acc)
   (if (zerop x)
       acc
       (if (minusp x)
           nil
           (factorial (1- x) (* acc x)))))

やってることはフィボナッチの二番目と同じ.

これから

マクロは上に上げたものよりつまらない実験しかしてないので紹介しませんがマクロやクロージャで遊んでたらなんか色々できそうな気がしてきたのでなにか作ってみたいなと感じています.レポート書くときにCで数値計算させてLaTeXのテーブルを出力するくらいしかプログラムしたことがないから,Noobが(あえて)Lispでなにか作るてきなことをやってみようかなって思ってます.(今思えばよくCでやってたなと思わなくもない)

感想

はてなシンタックスハイライト強い!!!

要領が悪い人とはたらく

要領が悪いということ

要領が悪いということはどういうことだろうか.一般に要領が悪いというのは他人に対する主観的な評価であるが,要領が悪いと感じる人はだいたい誰から見ても要領が悪い.これは要領が悪いとうことに対してある程度明確な基準が存在するからではないかと思う.最近,バイト先で100人の人に聞いたら99人は要領が悪いというに違いない人(残りの一人は本人)と仕事をしているので,要領が悪いということをよく考えさせられる.要領が悪いということを知るのは,今後彼と,あるいは別の要領が悪い人間と仕事をしていくのを助けてくれる.

要領が悪い人の基準

要領が悪い人,と聞くとどのような特徴を持った人間を思い浮かべるだろうか.単にいつまで経っても仕事ができない人,仕事を覚えるのが遅い人のことをこういうことが多いように思う.この根っこにはどのような原因があるだろうか.1年半に渡る観察活動の結果,要領が悪いと言われるような人には次のような特徴があると思われる.

  • 優先順位をつけることができない
    • 何事もディティールから入る
    • 引いた視点で全体の流れを把握することができない
    • 状況に則した判断を下す能力に乏しい
      • 故にすべての仕事はパターン化される
      • パターンから外れると何もできない
      • 優先順位がいつものパターンから変わるとわけのわからない行動を取る
  • 自分自身の能力が正しく理解できていない
    • 自分が何をすれば全体の効率が上がるのかわからない

要領が悪いと言われる所以は上に上げた点に起因するように思う.要領が悪い人間は(少なくとも仕事全体をモジュールの組みたてであるとするならば)一つ一つの仕事ができないわけではない.ただ,その組み立て方が理解できないし,考えることもできない.すべての仕事の部品はパターンとして理解できているのにその部品の使い方がわからないから仕事ができていないように見えるのだ.

要領が悪い人とはたらく

先に述べた特徴を元にどうすれば要領が悪い人と要領よく仕事ができるのか考えてみよう.私がこの一年半で編み出すことが出きた要領が悪い人とストレスなく仕事する方法はただひとつである.

その都度すべての事柄に対してこまめかつ丁寧に指示を出す

実に面倒くさい方法だと思うかもしれないが,要領が悪い人間には基本的にその根本を改善することはできない.前項でのべた特徴がお前のできないことだ,と諭してやっても,理解してもらえないだろうし,理解したとしても生まれてから今までの生き方がそう簡単に変わるわけがないのと同じように,簡単に仕事の仕方,物事の考え方を変えることはできない.

改善が望めないことに労力を割くよりは対症療法でもマニュアルチックに一から十まで手伝ってやることである.そうすればより多くのパターンがその人の中にだんだんと作られていって.最終的にはよくある通常的な業務パターンであればそつなくこなせるようになるだろう.先ほどの部品の例でたとえるならば,彼が(あるいは彼女が),持っている部品を組み立ててより大きな部品を作り,その部品の使い方を覚えるのを手助けしてやるということだ.彼らは,部品の使い方はわかるが組み立て方がわからないのである.できないことはできる人がやればいい.そうして普段のルーチンワークが大きな部品として組み上がれば多少は使いモノになるだろう.

何かイレギュラーが起こった場合は,また指示を出してやればよいのである.

コンビニはむずかしい

コンビニでものを売るということ

コンビニの商品は安くない.価格の面で考えるならば,大量仕入れを行っている大規模な小売店に勝つことは難しい.それでも多くのコンビニは一定の客数を維持し,利益を上げている.それは,消費者にとって,大規模な小売店ではできない利点がコンビニには存在しているということである.では,コンビニにはどのような利点があるだろうか.

  1. 24時間営業
  2. 店舗への移動の容易さ
  3. 商品の入れ替えの速さ
  4. 短時間で買い物を終了できる店舗面積の狭さ

ざっと次の四点が上げられる.1,3,4については本来大規模な小売店と比較すると本来デメリットとなる点をうまく活用した例であるように思う.1)少ない人件費で維持できる店舗面積により,店舗設備の維持費が無駄になる深夜帯の営業が可能になる 3)少量仕入れ,売り切りの納品スタイルによって,大量仕入れに比べて仕入れ値は上がるが,新商品の回転速度が高い 4)最低限多くの人が必要とするものを取り揃えることで,品揃えできるアイテムの限界を逆に手軽さとしている.

つまり,コンビニは商品ではなくサービスを売っている.もちろん商品も売っているが,コンビニという形態が成り立たせる本質は商品を仕入れ値より高く売ることではなく,商品にサービスという付加価値をつけて売ることである.それは家から徒歩で簡単にける距離に店舗があることだったり,新商品がすぐに売られることだったり,真夜中でもタバコとビールが手に入ることだったりするわけだ.

コンビニの売上を成長させるには

前項で述べた2については客にとってはメリットであるが,各店舗にとっては成長を阻害する厄介な点であるように思う.例えば,自分の家の近くにコンビニが2軒あり,片方が100mもう片方が500mの距離にあるとしよう.この時あなたはちょっとお昼ごはんをコンビニで済ませようと思った時どちらの店舗に行くだろうか.遠いほうが自分の好きなチェーンだったり,近いほうに自分が嫌いな店員がいたとしても,特別遠いほうでしか買えないものでもない限り,近くの店舗に買い物に行くのではないかと思う.少なくとも私はそうだ.もちろんそうでない人もいるだろうが,長期的に見るならば,どちらの店舗に行くのか偏りがあるだろう.(私は家から同じくらいの距離にミニストップセブン-イレブンがあるが,ほぼ100%セブン-イレブンに行く.セブン-イレブンのほうがどちらかというと近いからね.)各店舗にとって厄介なのはこの点である.立地,つまりどこに店を出すかによってその店舗にやってくる客数というのは,外部のイベント(近場の工事や祭り事など)を除外して考えるなら正規分布的になると予想できる.コンビニの売上は簡単に次のように計算できる.


売上=客単価 \times 客数

この式において客数は基本的に立地によってある程度固定されるから,店舗の売上を成長させるには客単価を伸ばす必要がある.客が買うつもりのなかったものを買う気にさせるテクニックは様々ある.基本的なものは特別意識しなくてもコンビニという形態をとってる時点で必然的に行われている.例えば,多くの客が目的買いの対象としている商品を店舗の外周に配置することで店舗全体を歩かせ,動線に衝動買いが期待できる商品を配置することで追加の購入を誘うテクニックはどのチェーンでも店舗の構造的に自動的に行われていることだろう.こういったものも,客が何をコンビニに求めているかを予想することでより効果的に活用できるのではないか.例えば,ほかよりも早く売り場に出される新商品は衝動買いの対象になる.狭い店舗面積では日用品の買い忘れに気付くのは容易だろう.そのチェーンでしか売っていない価値の高い商品は衝動買いだけでなく目的買いもされるだろうし,近隣の競合他店から一時的に客を掠めとるのに有効である.コンビニの本質が商品でなくサービスを売ることだと認識すれば各店舗独自の戦略を編み出すのに役に立つ.

コンビニのこれから

元締めのフランチャイザーが利益を伸ばすのは簡単なことだ.他チェーンの店舗の近くに自分のチェーンの店舗を出店させて少しでも他チェーンの店舗の売上をかすめ取れれば,あるいは競合が少ない地域を独占できればロイヤリティによる収入は増えるのだから,躍起になって店舗数を増やそうとする.

首都圏のコンビニは飽和していると見てもいいだろう.五分も歩けば何かしらのコンビニを発見できるのだから,これ以上新店を隙間に出し続けていてもフランチャイジーオーナー同士の潰し合いにしかならないのではないかと思う.過度なドミナント戦略フランチャイジーの不信感を高めるのではないか.私の知っている例だと地域に根強い人気がある某チェーンの店舗の近くに別のチェーンが連続で出店したことがあった.その件で特をしたのは出店したほうのフランチャイザーと,もしも店が立ちゆくだけの利益が得られているのであれば新店のオーナーくらいだろう.立地によって客数の上限はある程度決定されるのだから,もしもあなたがフランチャイジーとして新たにコンビニ経営を始めようと考えているのであれば,すでに近場に店舗が存在する立地は最大の客数を活用することはできず,それだけでリスクと判断するに十分であることは頭の隅においておくべきである.

このことは各フランチャイザーも考慮しているようで,最近は地方出店にかなり力を入れているようだ.日本地図,あるいは世界地図を300m間隔でコンビニがうめつくすまではコンビニフランチャイザーの成長は続くだろう.それからはお互いに潰し合うしかない.不幸なフランチャイジーは元締めの代わりに負債を背負うことになる.