SlideShare a Scribd company logo
Lisp tutorial for Pythonista.
Day #3 : Lisp meets web.




                                                          Ransui Iso
                           Strategic Technology Group, X-Listing Co, Ltd.
宿題やった?
今日は Web アプリだよ!

   超基本部分しかやらないけど
モチベーション維持のためにもネタは重要
Allegro Serve ってのがある
  有名な商用処理系の Allegro Common Lisp に
ついてくる Web サーバとちょっとしたフレームワーク
Portable Allegroserve
      ってのを使います
 Allegro serve を他の処理系でも使えるように
移植したやつがあるので、有り難く使わさせて頂く
インストールは簡単

   最初の日にインストールした QuickLisp を使うです


CL­USER> (ql:system­apropos "aserve")
#<SYSTEM aserve / portableaserve­20101006­cvs / quicklisp 2010­12­07>
NIL
CL­USER> (ql:quickload "aserve")

        ****  なんかいろいろでる ****

("aserve")
CL­USER> 
QuickLisp の更新方法

 リポジトリとかたまに更新されるのでチェックしとけ!

CL­USER> (ql:update­client)
Installed version 2010121400 is as new as upstream version 2010121400. 
No update.
T
CL­USER> (ql:update­all­dists)
No update available for "quicklisp 2010­12­07".
NIL
REPL 環境下でテストしてみる

●   とりあえずお約束の Hello World から
CL­USER> (require :aserve)
NIL
CL­USER> (use­package :net.aserve)
T
CL­USER> (use­package :net.html.generator)
T
CL­USER> (start :port 4040)
#<WSERVER port 4040 {1002D47421}>
CL­USER> (defun hello­world (request entity)
           (with­http­response (request entity)
             (with­http­body (request entity)
               (princ "Hello World" *html­stream*))))
HELLO­WORLD
CL­USER> (publish :path "/hello­world"
                  :content­type "text/plain; charset=utf8"
                  :function #'hello­world)

    で、ブラウザで http://localhost:4040/hello­world にアクセス
UTF8 なんだし日本語出るだろ jk

●   .sbclrc に設定もしてるし大丈夫じゃね?
CL­USER> (defun hello­world (request entity)
           (with­http­response (request entity)
             (with­http­body (request entity)
               (princ " 波浪ワールド " *html­stream*))))
UTF8 なんだし日本語出るだろ jk

●   .sbclrc に設定もしてるし大丈夫じゃね?
CL­USER> (defun hello­world (request entity)
           (with­http­response (request entity)
             (with­http­body (request entity)
               (princ " 波浪ワールド " *html­stream*))))




2­aserve­worker: 01/04/11 ­ 17:56:08 ­ while processing command "GET 
/hello­world HTTP/1.1"
got error The value 27874 is not of type (UNSIGNED­BYTE 8).




            多バイト文字文化圏の悲哀!
unicode/byte 文字列変換
●   最終的に byte 列として解釈できりゃ OK らしい
(defun bin­to­str (data)
  (let* ((size (length data))
         (octets (make­array size 
                             :element­type '(unsigned­byte 8)
                             :fill­pointer 0)))
    (dotimes (i size)
      (vector­push (char­code (elt data i)) octets))
    (sb­ext:octets­to­string octets :external­format :utf­8)))

●   ついでなので逆変換もいっとけ
(defun str­to­bin (str)
  (let* ((octets (sb­ext:string­to­octets str 
                         :external­format :utf­8
                         :null­terminate nil))
         (size (length octets))
         (result (make­array size 
                             :element­type 'character
                             :fill­pointer 0)))
    (dotimes (i size)
      (vector­push (code­char (elt octets i)) result))
    result))
また色々新しいの出た!
 例によってザックリと解説
let と let* の違い

●   let は変数束縛が同時に起こる感じ
       ●
(let ((x 10)
      (y 20)
       ●

      (z (+ x y))
       ●

      ●
  ... body ... )
      ●

      ●   z の初期化には x と y が必要だけど、初期化は * 同時 * なので x,y
            ともにまだ存在しない!なので未定義変数参照エラー。

●   let* は変数束縛が逐次的に起こる感じ
(let* ((x 10)
       (y 20)
       (z (+ x y))

  ... body ... )

      ●   こっちは問題ない。この差は多分にコンパイラの都合。
配列の作成

●   make-array で作成する
(make­array  次元指定 or 最大サイズ
              :element­type  データ型
              :initial­contents  初期値
              :fill­pointer  要素の追加位置ポインタを使うか?
              :adjustable  サイズを可変にするか?
                                  他にもオプションあるけど、普通使うのはこんくらい

(setf arr (make­array 3 :element­type 'simple­string
    :initial­contents '("ham" "spam" "egg"))

(serf arr (make­array '(2 2) :element­type 'integer
    :initial­contents '((1 0) (0 1)))

(setf arr (make­array 0 :fill­pointer t :adjustable t))
配列の参照と書き換え

●   単純に参照する場合は aref か elt を使う
(setf arr (make­array 3 :element­type 'integer))
(setf (aref arr 0) 0)
(setf (elt arr 1) 5)
(setf (aref arr 5) 10) ← 当然のことながらエラー
                         
               aref は配列専用で elt はシーケンス汎用。 aref のほうが効率はいいはず。

●   fill-pointer を使うと色々便利
(setf arr (make­array 3 :element­type 'integer :fill­pointer t))
(fill­pointer arr)
(setf (fill­pointer arr) 0)
arr
(vector­push 1 arr)
(vector­push 2 arr)
(vector­push 3 arr)
(vector­push 4 arr) ← 配列の長さを超えたので値は追加できない
                      
          :adjustable を t に指定して vector­push­extend を使えば可変長配列として
          要素ををどんどん追加できる。
dotimes フォーム

●   単純なカウンタ付きループ
(dotimes (var max­value result­value) body)

      ●   変数 var は 0 からスタートして (­ max­value 1) まで回る

      ●   result­value でループが終了したときの dotimes フォームの戻
           り値を指定できる。省略時は nil 。

      ●   body は普通に色々書けばいい。
           –   ループを脱出したい場合は return を使う。この場合 result­value は使われ
                ない。

           –   return と書くのが気持ち悪い人は
               (defmacro break­loop (&body body) `(return ,@body))
               とか定義しておくとちょびっと幸せかも。

           –   ちなみに break って名前はすでに cl:break として使われてて、デバッガへ
                行くとかいう機能になってるですと!こんなイイ名前をもったいない!
他のパッケージ内のシンボルの参照

●   2 つの参照方法がある
     ●   package­name:symbol­name
          –   公開 (export されている ) シンボルを参照する

     ●   package­name::symbol­name
          –   非公開 (export されていない ) シンボルを強制的に参照する




sb-ext パッケージ                SBCL の拡張機能
sb-impl パッケージ               SBCL の内部実装へのインタフェース
さっきまでのプログラムをファイルに書く

●   pastebin 見てね
     ●   http://pastebin.com/7AfPE0GG
     ●   helloworld.lisp  とか名前をつけて保存


●   実行と終了
     ●   sbcl ­­load helloworld.lisp
     ●   REPL が生きてるので (quit) で終了できる

●   リロード
     ●   REPL で (load "helloworld.lisp")
     ●   (publish­pages)
パッケージに関する操作

●   (require :module­name)
      ●   標準パッケージはこれでロードできる。

●   (asdf:oos 'asdf:load­op :module­name)
      ●   インストールされているモジュールをロードする
      ●   QuickLisp でインストールしたモジュールも内部では ASDF の管
           理下にある

●   (use­package :package­name)
      ●   Python で言うところの from package­name import * に似
           ているけど、名前空間の操作しかしない点が異なる。
HTML で出力してみる

●   html マクロを使う
(defun hello­html­world (request entity)
  (with­http­response (request entity)
    (with­http­body (request entity)
      (html (:html
              (:head (:title (u " 波浪ワールド ")))
              (:body 
               (:h1 (u " 波浪ワールド "))
               ((:p :style "color: red;") (u " 赤い文字ですよ "))
               ((:p :style (format nil "color: ~a;" 
                                   (nth (random 5) 
                                        '("red" "blue" 
                                           "green" "purple" "black"))))
                (u " ここはランダムで色が変わるのです ")) ))) )))


    Lisp の中にテンプレートが完全に組み込まれてますよ!
          テンプレの中に処理もそのまま書ける!
HTML マクロの基礎

●   (:tag­name body)
      ●   タグ名はキーワードで指定する
      ●   タグに囲まれる値部分はリストの要素として書く

●   ((:tag­name :attr­name value) body)
      ●   タグに属性をつけたい場合はタグ全体をリスト化して属性名をキー
           にしたキーワード引数風味に書く
      ●   値部分は普通の書き方と同じ

●   テンプレ的機能
      ●   body とか value 部分には「バイナリ文字列」を返す S 式が書ける
           –   便宜的にバイナリ文字列って言ってるよ
           –   いまんとこはマルチバイト文字想定される時は u マクロで囲んどけ。
フォームの取り扱い

●   入力欄は普通に書けばイイ
(defun hello (request entity)
  (with­http­response (request entity)
    (with­http­body (request entity)
      (html (:html
              (:head (:title (u " あいさつ ")))
              (:body
               (:h1 (u " あいさつ "))
               ((:form :method "POST" :action "greeting")
                ((:input :type "text" :name "name" :value ""))
                ((:input :type "submit" :name "greeting­button" 
                              :value "Hi!")) ) )) ))))
フォーム値の取り出し方法

●       request 引数に入ってるので引っ張り出す
(defun get­field­value (request field­name)
  (cdr (assoc field­name (request­query request) :test #'equal)))

    –   assoc 関数:連想リストを検索する
         ●   連想リストとは
             ((key1 value1) (key2 value2) ...)
             形式のリスト。すごく単純な Key Value データの表現。
         ●   線形検索なので当然のことながら効率は良くない。
         ●   が、 hash­table に比べればずっとお手軽。

    –   request­query 関数は
             ((name1 . value1) (name2 . value2) ...)
             形式の dot pair のリストでフォーム値を返す
         ●   dot pair については先週のスライド見てね
テンプレへの値の埋め込み

●   :princ­safe キーワードを使おう
      ●   < > & をエスケープしてくれるので安全
(defun greeting (request entity)
  (let ((name (get­field­value request "name")))
    (if (null name)
        (setf name " 名無しさん ")
        (setf name (bin­to­str name)))

    (with­http­response (request entity)
      (with­http­body (request entity)
        (html (:html
                (:head (:title (u " あいさつ ")))
                (:body
                 (:h1 (u " あいさつ "))
                 (:p (:princ­safe (u (format nil "Hello ~a" name)))) ))
)))))

(format t ... )   : 標準出力ストリームへ書き出す
(format nil ... ) : ストリームへの書き出し無し
お待ちかねの課題です
こんなの作ってね!
            超シンプル掲示板!
書きこまれたデータの保持

●   SQL 系は次回やる予定なので、今回はオンメモリ

(defvar *bbs­datas* nil)

(defun add­new­article (subject body)
  (push (cons subject body) *bbs­datas*))
テンプレートの構成



            書き込みエリア




                      ページ全体
            表示エリア
ページ全体テンプレート

●   こんな感じかな?

(defun page­template ()
  (html
    (:html
      (:head (:title "Tiny BBS"))
      (:body
       (write­section­template)
       (dolist (article *bbs­datas*)
         (view­template article))))))
表示部分のテンプレ

●   こんな感じ

(defun view­template (article)
  (html
    ((:div :style "border: 1px solid black; padding: 2px;")
     ((:div :style "background­color: lightgray; color: black;") 
      (:princ­safe (u (car article))))
     (:pre (:princ­safe (u (cdr article))))) ))
で、あと書かなきゃいけないのは
   書き込み部分のテンプレ
   コントローラ関数 :bbs
コントローラ関数 bbs の作り方のヒント

●   request からフォーム値を取り出す
     ●   書き込みボタンが押されてたら
         –   add-new-article を呼ぶ

     ●   ページ全体テンプレを呼ぶ

     ●   おしまい!
いやー。ヒント出しすぎ
 もう、余裕で作れるでしょ?

More Related Content

PDF
Lisp tutorial for Pythonista : Day 2
PDF
Material
PDF
たのしい関数型
PDF
これから Haskell を書くにあたって
PDF
クロージャデザインパターン
PDF
ゲーム開発者のための C++11/C++14
PDF
C++コミュニティーの中心でC++をDISる
PDF
15分でざっくり分かるScala入門
Lisp tutorial for Pythonista : Day 2
Material
たのしい関数型
これから Haskell を書くにあたって
クロージャデザインパターン
ゲーム開発者のための C++11/C++14
C++コミュニティーの中心でC++をDISる
15分でざっくり分かるScala入門

What's hot (20)

PDF
Google Developer Day 2010 Japan: プログラミング言語 Go (鵜飼 文敏)
PPTX
【java8 勉強会】 怖くない!ラムダ式, Stream API
PDF
これからの「言語」の話をしよう ―― 未来を生きるためのツール
PPTX
BoostAsioで可読性を求めるのは間違っているだろうか
PDF
F#入門 ~関数プログラミングとは何か~
PPTX
PDF
60分で体験する Stream / Lambda
 ハンズオン
PDF
C++ Template Meta Programming の紹介@社内勉強会
PDF
Pfi Seminar 2010 1 7
PPTX
Visual C++で使えるC++11
PDF
Java SE 8 lambdaで変わる プログラミングスタイル
PDF
BOF1-Scala02.pdf
PDF
並行プログラミングと継続モナド
PDF
Unity2015_No10_~UGUI&Audio~
PDF
JavaScript 講習会 #1
PDF
templateとautoの型推論
PDF
Haskell超入門 Part.1
KEY
Sml#探検隊
PDF
C++11概要 ライブラリ編
ODP
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
Google Developer Day 2010 Japan: プログラミング言語 Go (鵜飼 文敏)
【java8 勉強会】 怖くない!ラムダ式, Stream API
これからの「言語」の話をしよう ―― 未来を生きるためのツール
BoostAsioで可読性を求めるのは間違っているだろうか
F#入門 ~関数プログラミングとは何か~
60分で体験する Stream / Lambda
 ハンズオン
C++ Template Meta Programming の紹介@社内勉強会
Pfi Seminar 2010 1 7
Visual C++で使えるC++11
Java SE 8 lambdaで変わる プログラミングスタイル
BOF1-Scala02.pdf
並行プログラミングと継続モナド
Unity2015_No10_~UGUI&Audio~
JavaScript 講習会 #1
templateとautoの型推論
Haskell超入門 Part.1
Sml#探検隊
C++11概要 ライブラリ編
C++でのゲームプログラミングをしたときのお話 札幌C++勉強会 #4 〜スタートゲームプログラミング〜
Ad

Viewers also liked (7)

PPTX
重建街的故事
PDF
Lisp tutorial for Pythonista : Day 1
PDF
Lisp Tutorial for Pythonista : Day 4
PPT
프레젠테이션1
PDF
Lisp Tutorial for Pythonista : Day 5
PDF
Curriculum Vitae Morello Orfeo
PPS
Danza lenta
重建街的故事
Lisp tutorial for Pythonista : Day 1
Lisp Tutorial for Pythonista : Day 4
프레젠테이션1
Lisp Tutorial for Pythonista : Day 5
Curriculum Vitae Morello Orfeo
Danza lenta
Ad

Similar to Lisp Tutorial for Pythonista : Day 3 (20)

PDF
Lisp Tutorial for Pythonista Day 6
ODP
括弧への異常な愛情 または私は如何にして心配するのを止めてCommon Lispを愛するようになったか
PPT
2008.10.18 L4u Tech Talk
PDF
Lisp study
PDF
Lispmeetup #53 PythonベースのLisp方言、 Hyのすすめ
ODP
Real World OCamlを読んでLispと協調してみた
PDF
Shibuya.lisp #28: 仮題: R について
PDF
Clojure
PDF
Erlangやってみた
ODP
Introduction to Erlang/OTP
PDF
PythonでLispを実装した (evalつき)
KEY
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
PDF
スタートHaskell2 型を信じろ
PDF
Shibuyalisp6lt
PDF
Start haskell zipper
PDF
関数型言語テイスティング: Haskell, Scala, Clojure, Elixirを比べて味わう関数型プログラミングの旨さ
PDF
eZ Publish勉強会9月〜テンプレート言語〜
ODP
webを飾る技術
PDF
みんなもっとツール作ろうよ
PPTX
Control.Arrow
Lisp Tutorial for Pythonista Day 6
括弧への異常な愛情 または私は如何にして心配するのを止めてCommon Lispを愛するようになったか
2008.10.18 L4u Tech Talk
Lisp study
Lispmeetup #53 PythonベースのLisp方言、 Hyのすすめ
Real World OCamlを読んでLispと協調してみた
Shibuya.lisp #28: 仮題: R について
Clojure
Erlangやってみた
Introduction to Erlang/OTP
PythonでLispを実装した (evalつき)
How wonderful to be (statically) typed 〜型が付くってスバラシイ〜
スタートHaskell2 型を信じろ
Shibuyalisp6lt
Start haskell zipper
関数型言語テイスティング: Haskell, Scala, Clojure, Elixirを比べて味わう関数型プログラミングの旨さ
eZ Publish勉強会9月〜テンプレート言語〜
webを飾る技術
みんなもっとツール作ろうよ
Control.Arrow

More from Ransui Iso (10)

PPTX
「Pythonでやってみた」~広がるプログラミングの愉しみ~
PDF
Pythonで作る俺様サウンドエフェクター
PDF
アドテクを支える人と技術
PDF
Playing with curses
PDF
小中学生Hack-a-thonにオッサンが乗り込んだ話
PDF
XML-RPC : Pythonが「電池付属」と呼ばれる理由
PDF
ソフトシンセを作りながら学ぶPythonプログラミング
PDF
Introduction of ToySynth
PDF
PyQtではじめるGUIプログラミング
PDF
PySynth : A toy pure python software synthesizer.
「Pythonでやってみた」~広がるプログラミングの愉しみ~
Pythonで作る俺様サウンドエフェクター
アドテクを支える人と技術
Playing with curses
小中学生Hack-a-thonにオッサンが乗り込んだ話
XML-RPC : Pythonが「電池付属」と呼ばれる理由
ソフトシンセを作りながら学ぶPythonプログラミング
Introduction of ToySynth
PyQtではじめるGUIプログラミング
PySynth : A toy pure python software synthesizer.

Lisp Tutorial for Pythonista : Day 3

  • 1. Lisp tutorial for Pythonista. Day #3 : Lisp meets web. Ransui Iso Strategic Technology Group, X-Listing Co, Ltd.
  • 3. 今日は Web アプリだよ! 超基本部分しかやらないけど モチベーション維持のためにもネタは重要
  • 4. Allegro Serve ってのがある 有名な商用処理系の Allegro Common Lisp に ついてくる Web サーバとちょっとしたフレームワーク
  • 5. Portable Allegroserve ってのを使います Allegro serve を他の処理系でも使えるように 移植したやつがあるので、有り難く使わさせて頂く
  • 6. インストールは簡単 最初の日にインストールした QuickLisp を使うです CL­USER> (ql:system­apropos "aserve") #<SYSTEM aserve / portableaserve­20101006­cvs / quicklisp 2010­12­07> NIL CL­USER> (ql:quickload "aserve")         ****  なんかいろいろでる **** ("aserve") CL­USER> 
  • 8. REPL 環境下でテストしてみる ● とりあえずお約束の Hello World から CL­USER> (require :aserve) NIL CL­USER> (use­package :net.aserve) T CL­USER> (use­package :net.html.generator) T CL­USER> (start :port 4040) #<WSERVER port 4040 {1002D47421}> CL­USER> (defun hello­world (request entity)            (with­http­response (request entity)              (with­http­body (request entity)                (princ "Hello World" *html­stream*)))) HELLO­WORLD CL­USER> (publish :path "/hello­world"                   :content­type "text/plain; charset=utf8"                   :function #'hello­world) で、ブラウザで http://localhost:4040/hello­world にアクセス
  • 9. UTF8 なんだし日本語出るだろ jk ● .sbclrc に設定もしてるし大丈夫じゃね? CL­USER> (defun hello­world (request entity)            (with­http­response (request entity)              (with­http­body (request entity)                (princ " 波浪ワールド " *html­stream*))))
  • 10. UTF8 なんだし日本語出るだろ jk ● .sbclrc に設定もしてるし大丈夫じゃね? CL­USER> (defun hello­world (request entity)            (with­http­response (request entity)              (with­http­body (request entity)                (princ " 波浪ワールド " *html­stream*)))) 2­aserve­worker: 01/04/11 ­ 17:56:08 ­ while processing command "GET  /hello­world HTTP/1.1" got error The value 27874 is not of type (UNSIGNED­BYTE 8). 多バイト文字文化圏の悲哀!
  • 11. unicode/byte 文字列変換 ● 最終的に byte 列として解釈できりゃ OK らしい (defun bin­to­str (data)   (let* ((size (length data))          (octets (make­array size                               :element­type '(unsigned­byte 8)                              :fill­pointer 0)))     (dotimes (i size)       (vector­push (char­code (elt data i)) octets))     (sb­ext:octets­to­string octets :external­format :utf­8))) ● ついでなので逆変換もいっとけ (defun str­to­bin (str)   (let* ((octets (sb­ext:string­to­octets str        :external­format :utf­8                          :null­terminate nil))          (size (length octets))          (result (make­array size                               :element­type 'character                              :fill­pointer 0)))     (dotimes (i size)       (vector­push (code­char (elt octets i)) result))     result))
  • 13. let と let* の違い ● let は変数束縛が同時に起こる感じ ● (let ((x 10)       (y 20) ●       (z (+ x y)) ● ●   ... body ... ) ● ● z の初期化には x と y が必要だけど、初期化は * 同時 * なので x,y ともにまだ存在しない!なので未定義変数参照エラー。 ● let* は変数束縛が逐次的に起こる感じ (let* ((x 10)   (y 20)   (z (+ x y))   ... body ... ) ● こっちは問題ない。この差は多分にコンパイラの都合。
  • 14. 配列の作成 ● make-array で作成する (make­array  次元指定 or 最大サイズ :element­type  データ型 :initial­contents  初期値 :fill­pointer  要素の追加位置ポインタを使うか? :adjustable  サイズを可変にするか? 他にもオプションあるけど、普通使うのはこんくらい (setf arr (make­array 3 :element­type 'simple­string :initial­contents '("ham" "spam" "egg")) (serf arr (make­array '(2 2) :element­type 'integer :initial­contents '((1 0) (0 1))) (setf arr (make­array 0 :fill­pointer t :adjustable t))
  • 15. 配列の参照と書き換え ● 単純に参照する場合は aref か elt を使う (setf arr (make­array 3 :element­type 'integer)) (setf (aref arr 0) 0) (setf (elt arr 1) 5) (setf (aref arr 5) 10) ← 当然のことながらエラー   aref は配列専用で elt はシーケンス汎用。 aref のほうが効率はいいはず。 ● fill-pointer を使うと色々便利 (setf arr (make­array 3 :element­type 'integer :fill­pointer t)) (fill­pointer arr) (setf (fill­pointer arr) 0) arr (vector­push 1 arr) (vector­push 2 arr) (vector­push 3 arr) (vector­push 4 arr) ← 配列の長さを超えたので値は追加できない   :adjustable を t に指定して vector­push­extend を使えば可変長配列として 要素ををどんどん追加できる。
  • 16. dotimes フォーム ● 単純なカウンタ付きループ (dotimes (var max­value result­value) body) ● 変数 var は 0 からスタートして (­ max­value 1) まで回る ● result­value でループが終了したときの dotimes フォームの戻 り値を指定できる。省略時は nil 。 ● body は普通に色々書けばいい。 – ループを脱出したい場合は return を使う。この場合 result­value は使われ ない。 – return と書くのが気持ち悪い人は (defmacro break­loop (&body body) `(return ,@body)) とか定義しておくとちょびっと幸せかも。 – ちなみに break って名前はすでに cl:break として使われてて、デバッガへ 行くとかいう機能になってるですと!こんなイイ名前をもったいない!
  • 17. 他のパッケージ内のシンボルの参照 ● 2 つの参照方法がある ● package­name:symbol­name – 公開 (export されている ) シンボルを参照する ● package­name::symbol­name – 非公開 (export されていない ) シンボルを強制的に参照する sb-ext パッケージ SBCL の拡張機能 sb-impl パッケージ SBCL の内部実装へのインタフェース
  • 18. さっきまでのプログラムをファイルに書く ● pastebin 見てね ● http://pastebin.com/7AfPE0GG ● helloworld.lisp  とか名前をつけて保存 ● 実行と終了 ● sbcl ­­load helloworld.lisp ● REPL が生きてるので (quit) で終了できる ● リロード ● REPL で (load "helloworld.lisp") ● (publish­pages)
  • 19. パッケージに関する操作 ● (require :module­name) ● 標準パッケージはこれでロードできる。 ● (asdf:oos 'asdf:load­op :module­name) ● インストールされているモジュールをロードする ● QuickLisp でインストールしたモジュールも内部では ASDF の管 理下にある ● (use­package :package­name) ● Python で言うところの from package­name import * に似 ているけど、名前空間の操作しかしない点が異なる。
  • 20. HTML で出力してみる ● html マクロを使う (defun hello­html­world (request entity)   (with­http­response (request entity)     (with­http­body (request entity)       (html (:html               (:head (:title (u " 波浪ワールド ")))               (:body                 (:h1 (u " 波浪ワールド "))                ((:p :style "color: red;") (u " 赤い文字ですよ "))                ((:p :style (format nil "color: ~a;"                                     (nth (random 5)                                          '("red" "blue"    "green" "purple" "black"))))                 (u " ここはランダムで色が変わるのです ")) ))) ))) Lisp の中にテンプレートが完全に組み込まれてますよ! テンプレの中に処理もそのまま書ける!
  • 21. HTML マクロの基礎 ● (:tag­name body) ● タグ名はキーワードで指定する ● タグに囲まれる値部分はリストの要素として書く ● ((:tag­name :attr­name value) body) ● タグに属性をつけたい場合はタグ全体をリスト化して属性名をキー にしたキーワード引数風味に書く ● 値部分は普通の書き方と同じ ● テンプレ的機能 ● body とか value 部分には「バイナリ文字列」を返す S 式が書ける – 便宜的にバイナリ文字列って言ってるよ – いまんとこはマルチバイト文字想定される時は u マクロで囲んどけ。
  • 22. フォームの取り扱い ● 入力欄は普通に書けばイイ (defun hello (request entity)   (with­http­response (request entity)     (with­http­body (request entity)       (html (:html               (:head (:title (u " あいさつ ")))               (:body                (:h1 (u " あいさつ "))                ((:form :method "POST" :action "greeting")                 ((:input :type "text" :name "name" :value ""))                 ((:input :type "submit" :name "greeting­button"  :value "Hi!")) ) )) ))))
  • 23. フォーム値の取り出し方法 ● request 引数に入ってるので引っ張り出す (defun get­field­value (request field­name)   (cdr (assoc field­name (request­query request) :test #'equal))) – assoc 関数:連想リストを検索する ● 連想リストとは ((key1 value1) (key2 value2) ...) 形式のリスト。すごく単純な Key Value データの表現。 ● 線形検索なので当然のことながら効率は良くない。 ● が、 hash­table に比べればずっとお手軽。 – request­query 関数は ((name1 . value1) (name2 . value2) ...) 形式の dot pair のリストでフォーム値を返す ● dot pair については先週のスライド見てね
  • 24. テンプレへの値の埋め込み ● :princ­safe キーワードを使おう ● < > & をエスケープしてくれるので安全 (defun greeting (request entity)   (let ((name (get­field­value request "name")))     (if (null name)         (setf name " 名無しさん ")         (setf name (bin­to­str name)))     (with­http­response (request entity)       (with­http­body (request entity)         (html (:html                 (:head (:title (u " あいさつ ")))                 (:body                  (:h1 (u " あいさつ "))                  (:p (:princ­safe (u (format nil "Hello ~a" name)))) )) ))))) (format t ... ) : 標準出力ストリームへ書き出す (format nil ... ) : ストリームへの書き出し無し
  • 26. こんなの作ってね! 超シンプル掲示板!
  • 27. 書きこまれたデータの保持 ● SQL 系は次回やる予定なので、今回はオンメモリ (defvar *bbs­datas* nil) (defun add­new­article (subject body)   (push (cons subject body) *bbs­datas*))
  • 28. テンプレートの構成 書き込みエリア ページ全体 表示エリア
  • 29. ページ全体テンプレート ● こんな感じかな? (defun page­template ()   (html     (:html       (:head (:title "Tiny BBS"))       (:body        (write­section­template)        (dolist (article *bbs­datas*)          (view­template article))))))
  • 30. 表示部分のテンプレ ● こんな感じ (defun view­template (article)   (html     ((:div :style "border: 1px solid black; padding: 2px;")      ((:div :style "background­color: lightgray; color: black;")        (:princ­safe (u (car article))))      (:pre (:princ­safe (u (cdr article))))) ))
  • 31. で、あと書かなきゃいけないのは 書き込み部分のテンプレ コントローラ関数 :bbs
  • 32. コントローラ関数 bbs の作り方のヒント ● request からフォーム値を取り出す ● 書き込みボタンが押されてたら – add-new-article を呼ぶ ● ページ全体テンプレを呼ぶ ● おしまい!