孤独にそっくり

開いている窓の前で立ち止まるな

実践Common Lispを読みたかったけどダメだった(と思ったら大丈夫だった) 第24章~第26章途中まで

Angry
ずっとこんな顔してる
smile
後にこんな顔になった

第24章〜第25章

この章から真面目にメモ取りながら読んでも、ほとんどそのままの写しになってしまって意味がないので書くのがめんどくさくなってしまいました。
具体的に何をやっているのか、全体像がつかみにくいし、遅々として進まず少し萎えてきてしまったのもあります。
やっぱり自分の手で書いて動かさないとやってる気がしませんね。
とりあえずちゃんとメモを書いていたバイナリフォーマットについてだけ貼っときます。

バイナリフォーマットの基礎

符号なし16ビット整数を扱うならば、バイトを2つ読み込み、片方を256倍して、もう一方のバイトと加えて1つの数字にする。
Common LispではLDB関数があるから簡単にできる
整数内のビットの個数と位置を指定するには、BYTE関数を使う

;;最下位バイトを取り出す
(ldb (byte 8 0) #xabcd) -> 205 ;205 is #xcd
;;次のバイトを取得
(ldb (byte 8 8) #xabcd) -> 171 ;171 is #xab
;;元の数字は171*256+205=43981

;;setfを使えば、読み込んだ数字を格納できる
(defun read-u2 (in)
  (let ((u2 0))
    (setf (ldb (byte 8 8) u2) (read-byte in))
    (setf (ldb (byte 8 0) u2) (read-byte in))
    u2))

さらっと流し読みしてパス。

第26章 実践:AllegroServeでWebプログラミング

オープンソースのAllegroServeでWebプログラミングする

*30秒で分かるサーバサイドWebプログラミング

詳しくはhttp://www.jmarshall.com/easy/
WebサーバからWebページをリクエストしてレンダリングする。
ブラウザとサーバはHTTPと呼ばれるプロトコルを使って通信する。リクエストする際には、データを含めることもできる。
静的ページは単純なHTMLファイルで、動的ページはブラウザに要求されるたびに生成されるHTMLのことを示す。
大まかな流れとしては、ブラウザがリクエストを送り、サーバはそのリクエストを処理するコードを見つけてそれを実行し、クエリパラメータとクッキーを使って何かする。

*AllegroServe

Javaサーブレットみたいな感じ。ブラウザがリクエストするたびに、AllegroServeはリクエストをパースし、リクエストを処理するエンティティと呼ばれるオブジェクトを探す。
サーバの立ち上げ方の説明が書いてあるけどちんぷんかんぷん。

最初のステップはAllegroServeのコードを自分のLispイメージにロードすることだ。Allegroであれば単純に(require :aserve)とタイプすればいい。他のLisp(Allegroでもいいが)では、portableaserveディレクトリツリーの最上位にINSTALL.lispをロードすることで、PortableAllegroServeをロードできる...

みんなこれだけ読んで出来るんですか?しゅごい。。。
ぐぐってみたらcl-aserveってのを入れればいいっぽい?
http://blog.goo.ne.jp/seacormorant/e/2b64f8724b7ed2a2bbdc687961096ec0
http://d.hatena.ne.jp/rucifer_hacker/20090715/1247635919
適当にsudo apt-get install cl-aserveしたらずおーってなってインストールされた。
その後、SLIME上でまんまコピペしたら動いた。

CL-USER> (require :aserve)
...(省略)...
CL-USER> (net.aserve:start :port 8888)
#<NET.ASERVE:WSERVER port 8888 {C6B2191}>
CL-USER> (net.aserve:publish
          :path "/"
          :content-type "text/html; charset=utf-8"
          :function
          #'(lambda (req ent)
              (net.aserve:with-http-response
                  (req ent)
                (net.aserve:with-http-body
                    (req ent)
                  (net.html.generator:html
                    (:html
                      (:head (:title "Hello World!"))
                      (:body "Hello World!")))))))

エラーも起こらず、http://localhost:8888Hello World!してひとまず安堵しました。

その後に、パッケージを定義したらエラーを吐いた。

(defpackage :com.gigamonkeys.web
  (:use :cl :net.aserve :com.gigamonkeys.html :com.gigamonkeys.url-function))
;;エラー
The name "COM.GIGAMONKEYS.HTML" does not designate any package.
   [Condition of type SB-KERNEL:SIMPLE-PACKAGE-ERROR]

仕方ないからエラーをそのままググったら5件くらいしかヒットしなかったけど、解決方法を発見?
http://jun1484528313.blog.163.com/blog/static/2298290032015211112750418/
中国語?で書かれているけど、真似して'com.gigamonkeys.utilities入れてからパッケージ定義したらなんかできたっぽい。
何をしているのかはいまいち不明。

CL-USER> (ql:quickload 'com.gigamonkeys.utilities)
To load "com.gigamonkeys.utilities":                                           
  Load 2 ASDF systems:                                                         
    alexandria split-sequence                                                  
  Install 1 Quicklisp release:                                                 
    monkeylib-utilities                                                        
; Fetching #<URL "http://beta.quicklisp.org/archive/monkeylib-utilities/2012-09\-09/monkeylib-utilities-20120909-git.tgz">
; 11.86KB                                                                      
==================================================                             
12,148 bytes in 0.01 seconds (988.61KB/sec)
; Loading "com.gigamonkeys.utilities"
..................................................                             
[package com.gigamonkeys.utilities]...............                             
[package iso].....                                                             
(COM.GIGAMONKEYS.UTILITIES)
CL-USER> (defpackage :com.gigamonkeys.html
(:use :common-lisp :com.gigamonkeys.utilities)                    
             (:export :with-html-output                                     
                       :in-html-style                                    
                       :define-html-macro                            
                       :html                                     
                       :emit-html                            
                       :&attributes))
#<PACKAGE "COM.GIGAMONKEYS.HTML">  
CL-USER> (defpackage :com.mutou.web
(:use :cl :net.aserve :com.gigamonkeys.html))
#<PACKAGE "COM.MUTOU.WEB">
CL-USER> (in-package :com.mutou.web)
#<PACKAGE "COM.MUTOU.WEB">                                                     
WEB> 

;;サーバの起動
;;http://localhost:2001/にアクセス
;;Not Found
WEB> (start :port 2001)
127.0.0.1 - - [Fri, 12 Aug 2016 09:50:48 GMT] "GET / HTTP/1.1" 404 -1
127.0.0.1 - - [Fri, 12 Aug 2016 09:50:48 GMT] "GET /favicon.ico HTTP/1.1" 404 -
1  

Not Foundだけどサーバ起動はできた。
わりとめんどくさいので、ひとまずaserve.lispにコピペして
(load "aserve")
(in-package :com.mutou.web)
すればWEBパッケージに移行できるようにした。

hello.hmtlファイルを作成してアクセスしてみる。

CL-USER> (load "aserve")
To load "com.gigamonkeys.utilities":                                           
  Load 1 ASDF system:                                                          
    com.gigamonkeys.utilities                                                  
; Loading "com.gigamonkeys.utilities"                                          T                                                                              
CL-USER> (in-package :com.mutou.web)
#<PACKAGE "COM.MUTOU.WEB">                                                     
WEB> 
;;ポートを開く
WEB>(start :port 2001)
;;単体のページにアクセス
(publish-file :path "/hello.html" :file "systems/html/hello.html")
;;公開されたエンティティを消去
(publish-file :path "/hello.html" :remove t)
;;ディレクトリツリー内のすべてのファイルを公開
;;prefix引数は、パス部分の始まりを指定する
(publish-directory :prefix "/" :destination "systems/html/")

publish-directoryしたらそんなディレクトリは無いぞとエラー吐いたから
http://franz.com/support/tutorials/aserve-tutorial.htm#qs2-1
を眺めてみたけどよくわからない

;;ディレクトリを公開するときに出たエラー
WEB> (publish-directory :prefix "/" :destination "systems/html/")
5-aserve-worker: 08/14/16 - 11:15:03 - while processing command "GET /hello.htm
l HTTP/1.1"                                                                   
got error Error in SB-POSIX:STAT: そのようなファイルやディレクトリはありません (2)
#<NET.ASERVE::DIRECTORY-ENTITY {1002C1CAD3}>  
;;pathをちょっと変えたら動いた
;;~/がダメなのかなー
WEB> (publish-directory :prefix "/" :destination "/systems/html/")
*動的なコンテンツを生成

動的コンテンツの関数はpublishとpublish-prefixでそれぞれpublish-fileとpublish-directoryに対応している。
この関数は2つの引数を取る。リクエストを表現するためのオブジェクトと公開されるエンティティのオブジェクト。後者はあとでやるけどマクロを渡すくらいしか使わない。前者はブラウザから送られてくる情報を取得するのに使う。

;;リクエストごとに異なる乱数を表示するページを生成する関数
(defun random-number (request entity)
  (with-http-response (request entity :content-type "text/html")
    (with-http-body (request entity)
      (format
       (request-reply-stream request)
"<html>
<head><title>Random</title></head>
<body>
<p>Random number: ~d</p>
</body>
</html>
"
       (random 1000)))))

ずっと

Compile-time error:
during macroexpansion of (FORMATTER ほにゃらら〜
Use *BREAK-ON-SIGNALS* to intercept.   
error in FORMAT: unknown directive (character: Space)  

ってエラー吐いて死にそうになってたけど、「~@」外したら実行できた。

*HTMLを生成する

S式とHTMLは本質的に同型なので自然に生成できるらしい。

;;同じ意味
<p>foo</p>
(:p foo)

random-numberを書き直したものを実行してみたけど鬼のようにエラーが出る
http://docs.komagata.org/4204
こちらのブログで紹介されているソースを使ってみたけど、Hello Worldで同じように(:html)とか使って書いてるのにエラー吐いてない。
emit-html関数とかも使えない。

;;The function COM.GIGAMONKEYS.HTML:EMIT-HTML is undefined.
;;   [Condition of type UNDEFINED-FUNCTION]
(emit-html'(:html(:head (:title "Hello"))(:body (:p "Hello, world!"))))

COM.GIGAMONKETS.HTMLで定義されてないって。
よく考えたら:com.mutou.webってパッケージ使っているせいでは・・・

ちゃんと公式のasdで動かしてみる

;;まずはリンクを貼る
$ln -s practicals-1.0.3/Chapter26/url-function.asd
;;
(setf asdf:*central-registry*
             '(*default-pathname-defaults*
                #p"/systems/"
                #p"/usr/share/common-lisp/systems/"))
;;実行する
(asdf:operate 'asdf:load-op :url-function)
; Evaluation aborted on Component :HTML not found, required by #<SYSTEM "url-function">.

ダメだった。
パッケージ関連を理解していないせいで進まなすぎて心折れそう。
結論、対症療法は良くない。
思うに、The name "COM.GIGAMONKEYS.HTML" does not designate any package.ってエラーが出たとこらから地獄がはじまっている。
いったん仕切り直して第26章をどうにかしたいと思います。この泥沼を抜け出さないとこれ以降まったく実行できない状態になるので・・・
あとで読む
http://chaton.practical-scheme.net/common-lisp-jp/a/2011/11/03

P.352でCOM.GIGAMONKEYS.HTML(後ですぐ説明するパッケージ)って書いてあるのに説明してなくない・・・
あと、P.371で:com.gigamonkeys.macro-utilitiesは第18章で定義したパッケージ〜って書いてあるけど、第18章ってdefpackageしてなくない・・・
追記
とりあえず、全ての章のasdファイルを読み込んで実行してみたら、普通にできました!!!何でいままでこれをしてなかったのか逆にいみわからない!!!大変失礼致しました!!!!

README読んでみたらpracticals.asdっていう全体のファイルを読み込むか、個別に読み込んでくださいみたいなことが書いてあるっぽいので、最初からpracticalsでやればよかったけど、よくわからないから全部のasdにリンクはってasdfしたら普通に動いて今日一日頭を抱えていたのは何だったのかと思いました。大変お見苦しいところお見せしてしまいまして、申し訳ありません。全然難しくありませんでした。明日以降ちゃんと26章読む。自戒の念を込めて、この記事は残しておきます。