#:g1

values-list vs multiple-value-call

Posted 2012-09-30 14:57:59 GMT

mapしたものをvalues-listで返すのと、リストは作らないで多値を返す方法では、どっちが良いのか調べてみたかった。 多値が5つ位だったら、直接返してもコンスが少ないので元が取れるかも 多くなると、コンスは少ないけどかなり遅くなる

(defun values-map (fn list)
  (declare (function fn)
           (optimize (debug 1) (speed 3)))
  (if (endp list)
      (values)
      (multiple-value-call #'values
                           (funcall fn (car list))
                           (values-map fn (cdr list)))))

(defun values-map2 (fn list) (declare (function fn) (optimize (debug 1) (speed 3))) (values-list (mapcar fn list)))

(defvar *5-data*
  (srfi-1:iota 5))

(loop :repeat 100000 :do (values-map #'identity *5-data*)) ;⇒ NIL #|------------------------------------------------------------| Evaluation took: 0.036 seconds of real time 0.036002 seconds of total run time (0.036002 user, 0.000000 system) 100.00% CPU 87,716,718 processor cycles 0 bytes consed

Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz |------------------------------------------------------------|#

(loop :repeat 100000 :do (values-map2 #'identity *5-data*)) ;⇒ NIL #|------------------------------------------------------------| Evaluation took: 0.017 seconds of real time 0.020001 seconds of total run time (0.020001 user, 0.000000 system) 117.65% CPU 39,264,201 processor cycles 9,601,024 bytes consed

Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz |------------------------------------------------------------|#

(defvar *100-data*
  (srfi-1:iota 100))

(loop :repeat 100000 :do (values-map #'identity *100-data*)) ;⇒ NIL #|------------------------------------------------------------| Evaluation took: 2.183 seconds of real time 2.168135 seconds of total run time (2.168135 user, 0.000000 system) 99.31% CPU 5,226,740,100 processor cycles 0 bytes consed

Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz |------------------------------------------------------------|#

(loop :repeat 100000 :do (values-map2 #'identity *100-data*)) ;⇒ NIL #|------------------------------------------------------------| Evaluation took: 0.163 seconds of real time 0.160010 seconds of total run time (0.152009 user, 0.008001 system) [ Run times consist of 0.012 seconds GC time, and 0.149 seconds non-GC time. ] 98.16% CPU 388,223,343 processor cycles 161,615,360 bytes consed

Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz |------------------------------------------------------------|#


HTML generated by 3bmd in LispWorks 7.0.0

Sagittarius 0.3.6 インストールメモ

Posted 2012-09-14 12:43:00 GMT

Sagittariusで、SRFI 86が標準サポートされたらしいので早速インストール。
OS: Ubuntu Linux 11.10 x86_64

$ wget http://sagittarius-scheme.googlecode.com/files/sagittarius-0.3.6.tar.gz
$ tar xvf sagittarius-0.3.6.tar.gz
$ cd sagittarius-0.3.6/
$ cmake .
$ make
$ make test
$ make doc
$ make install

ドキュメンテーション: Sagittarius Users' Reference

(import (srfi :86))

(define fibo (lambda (n . a1+a2) (alet ((opt a1+a2 (a1 1) (a2 0))) (cond ((= 0 n) a2) ((= 1 n) a1) (else (fibo (- n 1) (+ a1 a2) a1))))))

(define (fib n) (alet (((n n) (a1 1) (a2 0) . *fib)) (cond ((= 0 n) a2) ((= 1 n) a1) (else (*fib (- n 1) (+ a1 a2) a1)))))

(fib 1000) ;=> 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875

SRFI 86の妙な局所関数の定義形式を久々に思い出した

Allegroのregexp:re-lambdaもどき

Posted 2012-08-31 14:57:59 GMT

必要でもなんでもなかったのですが、cl-ppcreのregister-groups-bindと、Allegroのregexp:re-lambdaの動作の違いを調べてたらできました。色々無理があります。

(in-package :ppcre)

;;; http://www.franz.com/support/documentation/9.0/doc/operators/excl/re-lambda.htm (defun normalize-var-list-re (list) ;; (var integer) -- var binds to integer-th submatch (0 for whole). ;; (var string/symbol) -- var binds to the submatch named by string/symbol. ;; var -- var binds to the submatch named by var. (mapcar (lambda (e) (if (consp e) (list (car e) (etypecase (cadr e) (symbol (string (cadr e))) ((or integer string) (cadr e)))) (list e (string e)))) list))

(defun list-flatten (tree) (let ((result '())) (labels ((*scan (item) (if (consp item) (map nil #'*scan item) (push item result)))) (*scan tree)) (nreverse result)))

(defun regname-pos-alist (regex-tree) ;; TODO: まあ安直にflattenして出現した順番ですわ (let ((u (list-flatten regex-tree))) (do ((e u (cdr e)) (ans '() ) (cnt 1)) ((endp e) ans) (case (car e) ((:named-register) (push `(,(cadr e) ,cnt) ans) (incf cnt)) ((:register :nop))))))

(defun regpos (pos-designator alist) (etypecase pos-designator (integer pos-designator) (string (second (assoc pos-designator alist :test #'string=)))))

(defmacro re-lambda (regex var-list &body body &aux start end sharedp (*allow-named-registers* t) (regname.pos (regname-pos-alist (parse-string regex)))) (with-unique-names (match-start match-end reg-starts reg-ends start-index substr-fn target-string if-does-not-match ) `(lambda (,target-string &key ((:if-does-not-match ,if-does-not-match) NIL)) (multiple-value-bind (,match-start ,match-end ,reg-starts ,reg-ends) (scan (let ((*allow-named-registers* t)) (create-scanner (parse-string ,regex))) ,target-string :start (or ,start 0) :end (or ,end (length ,target-string))) (declare (ignore ,match-end)) (if ,match-start (let* ,(list* `(,substr-fn (if ,sharedp #'nsubseq #'subseq )) (loop :for (var pos) :in (normalize-var-list-re var-list) :when var :collect (cond ((eql 0 pos) `(,var (funcall ,substr-fn ,target-string ,match-start ,match-end ))) ((setq pos (regpos pos regname.pos)) (let ((pos (1- pos))) `(,var (let ((,start-index (aref ,reg-starts ,pos ) )) (if ,start-index (funcall ,substr-fn ,target-string ,start-index (aref ,reg-ends ,pos ) ) nil ))))) (T `(,var nil)) ))) ,@body ) ,if-does-not-match )))))

(funcall (RE-LAMBDA "(?<Z>.)(?<Z>.)(?<X>.)(?<Y>.)" ((a 0) z x y) (LIST a z x y))
         "defabcz"
         :if-does-not-match t)
;=>  ("defa" "e" "f" "a")

(funcall (re-lambda "(abc|def)(.*)" ((a 0) (b 1) (c 2)) (list a b c)) "defabc") ;=> ("defabc" "def" "abc") (funcall (re-lambda "(?<FOO>[a-z]+)(?<BAR>\\d+)" (foo bar) (list foo bar)) " acl70beta ") ;=> ("acl" "70")

;;; サポートしてません (funcall (re-lambda "cde" ((a 0 :before) (b 0) (c 0 :after)) (list a b c)) "abcdefg") ;=> ("cde" "cde" "cde") ;;; Allegro: regex2 ;=> ("ab" "cde" "fg")


HTML generated by 3bmd in LispWorks 7.0.0

CLでsrfi-105

Posted 2012-08-28 02:02:00 GMT

CLでsrfi、今回は、srfi-105の「Curly-infix-expressions」です。
srfi-105は提案されたばかりで、まだ、ドラフトのsrfiです。
中置っぽく書けるという記法ですが、プログラム全体をこの記法で記述するというよりは、既存の書法に混ぜて使うもののように見受けられます。

動作

{3 + 3}
;=> 6

'{a * {b + c}} ;=> (* A (+ B C))

'{x eqv? `a} ;=> (EQV? X 'A)

'{(- a) / b} ;=> (/ (- A) B)

'{(f a b) + (g h)} ;=> (+ (F A B) (G H))

'{a + (f b) + x} ;=> (+ A (F B) X)

'{{a > 0} and {b >= 1}} ;=> (AND (> A 0) (>= B 1))

移植について

Schemeコンパチのパッケージを作っていたので、参照実装をほぼコピペしただけという感じです。
リードテーブルの管理には、named-readtablesを使ってみています。

ArcのcomposeをCLでがんばる

Posted 2012-08-24 17:21:00 GMT

Common Lispのリーダーマクロは大体の事はできますが、「∘」にリーダーマクロを設定することで

car∘cdr 
(compose car cdr)
に展開するようなことはできません。readが一つ前の式を保存するような機能があれば、簡単にできそうではありますが、残念ながらそういう機能はCommon Lispにはありません。
しかし、foo.bar.bazのようなメソッドチェーン的な表現を(foo (bar (baz)))としたいという需要は結構あるようで、どうやったらできるのかというのは、割とFAQな気がします。
読み取り以外にも色々組み合わせて苦肉の策を練るか、構文を若干妥協することになりますが、私個人としては、まあ、∘foo∘bar位なら妥協しても良いかなというところで、
(defun compose-reader (stream char)
  (declare (ignore char))
  `(arc:compose
    ,@(loop :for fctn := (read-preserving-whitespace stream t nil t)
             :then (progn (read-char stream t nil t)
                          (read-preserving-whitespace stream t nil t))
             :collect `#',fctn
             :while (eql (peek-char nil stream nil nil t) #\∘))))

(set-macro-character #\∘ #'compose-reader)

(mapcar ∘list∘(lambda (x) (* 2 x)) '(1 2 3 4)) ;=> ((2) (4) (6) (8))

位が落とし所かなと思ったりしています。

苦肉の策

苦肉の策としては、()に付いているリーダーマクロを変更したり、internを改造したり、with-〜で囲まれたところは有効になる、等々考えられますが、Ron Garret氏がRe: Anonymous packages (comp.lang.lisp)で紹介している、アルファベット全部にリーダーマクロを付けるという方法を思い出したので試しに作ってみました
(let ((r (copy-readtable nil)))
  (defun read-symbol (stream)
    (let* ((*readtable* r)
           (obj (read-preserving-whitespace stream)) )
      (typecase obj
        (symbol (let ((symname (ppcre:split #\∘ (symbol-name obj))))
                  (if (cdr symname)
                      (values (mapcar (lambda (s) `#',(intern s)) symname) t)
                      obj )))
        (otherwise obj) ))))

(defun compose-reader-macro-reader (stream char) (unread-char char stream) (multiple-value-bind (expr win) (read-symbol stream) (if win (let ((args (gensym))) `(lambda (&rest ,args) (declare (dynamic-extent ,args)) (apply (arc:compose ,@expr) ,args) )) expr )))

(map nil (lambda (c) (cl:set-macro-character c #'compose-reader-macro-reader t)) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_")

動作

(car∘car∘list∘list car∘cdr)
;=>  #<FUNCTION (COMMON-LISP:LAMBDA (COMMON-LISP:&REST #:G10378)) {10131BC51B}>

マクロ展開

((LAMBDA (&REST #:G10398)
   (DECLARE (DYNAMIC-EXTENT #:G10398))
   (APPLY (ARC:COMPOSE #'CAR #'LIST #'LIST) #:G10398))
 (LAMBDA (&REST #:G10399)
   (DECLARE (DYNAMIC-EXTENT #:G10399))
   (APPLY (ARC:COMPOSE #'CAR #'CDR) #:G10399)))
できなくもないですが、アルファベットにリーダーマクロを付けるというのが、やっぱり気持ち悪いです。
Arcのリーダーを眺めてみましたが、読み取った文字列をcompose(:)の文字で分解したりしている様なので、動きとしては、割とこっちに近かったりはするみたいです。

ちなみに、Franz Lispでは、中置のリーダーマクロが書けたようですが、

(defun plusop (x)
  (cond ((null x) (tconc nil '+))
        (t (lconc nil (list 'plus (caar x) (read))))))

(setsyntax '+ 'vinfix-macro 'plusop)

(a + b) ;=> (plus a b)

Common Lispも、これ位手軽に書けたら良かったですね

とりあえずCLで書く (2012-08-24)

Posted 2012-08-23 18:25:00 GMT

やりたいこと

私は、TwitterのつぶやきはSLIMEから投稿していて、面白いLisp系ブログがあったらリンクを流したりしていますが、同じ記事をRedditにも投稿することが多く、二度手間なのでどちらにも一度で投稿できるようにしたいな、というところ。とりあえず、Redditに投稿できる仕組みができれば、既存のものと適当に組み合わせて使えるので、それでOK。

(in-package :g1.arc)
(in-readtable :g1.arc)

(cl:defclass reddit-session () ((cookie :initform (cl:make-instance 'drakma:cookie-jar) :accessor reddit-session-cookie) (user-hash :initform nil :accessor reddit-session-user-hash)))

(cl:defvar reddit-session* (cl:make-instance 'reddit-session))

(cl:defvar reddit-user-login-info-file* (cl:merge-pathnames (cl:make-pathname :name ".REDDIT-LOGIN" :case :common) (cl:user-homedir-pathname) ))

(def reddit-post (url params) (let drakma:*drakma-default-external-format* :utf-8 (drakma:http-request url :method :post :cookie-jar (reddit-session-cookie reddit-session*) :parameters params)))

(def reddit-login (username) (cl:multiple-value-bind (body stat) (reddit-post (+ "https://ssl.reddit.com/api/login/" username) (cl:acons "api_type" "json" (w/infile in reddit-user-login-info-file* (cl:read in) ))) (and (is 200 stat) (json:json-bind (json.data.modhash) (babel:octets-to-string body) (= (reddit-session-user-hash reddit-session*) json.data.modhash )))))

(def reddit-submit (title url sr (o uh (reddit-session-user-hash reddit-session*)) (o kind "link")) (babel:octets-to-string (reddit-post "http://www.reddit.com/api/submit" (cl:pairlis '("title" "url" "sr" "uh" "kind") (list title url sr uh kind) ))))

コード解説

  • slimeで実行するので適当に投稿するコマンドを定義するだけ
  • ユーザー情報はお手軽にread
  • 自作イメージを使っているので、cl-json・babel・drakma・自作ユーティリティは、既にイメージ内にロードされている
  • make-pathnameは、:case :common / いまいち御利益は実感しておらず
  • drakma:http-requestは毎度ラッピングして使いたくなるけど、ラッピングするのもぱっとしない
  • json:json-bindなんてものがあるのを初めて知った。今迄マニュアル読んで使ってなかった
  • RedditのAPIはgithubを参照: API · reddit/reddit Wiki · GitHub
  • 無駄にArcっぽく書けるパッケージを自作してみたので、無理に使ってみている

ArcっぽいものとCLっぽいものを混ぜると、Arcっぽいところは短かくてコンパクトだけど、CLなところは横長だなーと思ったりします

インライン関数で型指定の真似事

Posted 2012-08-17 13:02:00 GMT

型指定だけするインライン関数を定義して、この関数を経由することにすれば、コンパイラも型の指定を理解できるのではないかと、ふと思い立ったので試してみました。

(in-package :cl-user)

(declaim (inline fix))

(defun fix (x) (the fixnum x))

(defun foo (x y) (declare (optimize (safety 0) (speed 3) (debug 0))) (fix (+ (fix x) (fix y))))

; disassembly for FOO (assembled 18 bytes) SAR RDX, 1 ; no-arg-parsing entry point SAR RDI, 1 ADD RDX, RDI SHL RDX, 1 MOV RSP, RBP CLC POP RBP RET

ちょっと変ですが、一応、型を見て最適化されてはいるようです。
関数呼び出しを展開しているためか、右シフト(fixnumのタグを外す)して足して左シフトという手順になっています

普通に型指定した場合は、

(defun barney (x y)
  (declare (optimize (safety 0) (speed 3) (debug 0)))
  (the fixnum (+ (the fixnum x)
                 (the fixnum y))))

; disassembly for BARNEY (assembled 9 bytes) ADD RDX, RDI ; no-arg-parsing entry point MOV RSP, RBP CLC POP RBP RET

となります。
rdx(1番目の引数/返り値)と、rdi(2番目の引数)を足して終わり

もうちょっと複雑なケースで、composeしてみたり

(declaim (inline comp2))

(defun comp2 (f g) (lambda (x y) (funcall f (funcall g x y))))

(defun blarg (x y) (declare (optimize (safety 0) (speed 3) (debug 0))) (funcall (comp2 #'fix #'+) (fix x) (fix y)))

; disassembly for BLARG (assembled 18 bytes) SAR RDX, 1 ; no-arg-parsing entry point SAR RDI, 1 ADD RDX, RDI SHL RDX, 1 MOV RSP, RBP CLC POP RBP RET

結果はfooとまったく同じ
インライン関数なので要するにマクロと同じで、当然といえば、当然なのですが、若干コンポーザブルなのではないかと。
Arcだったら
(def blarg (x y)
  (fix:+ fix.x fix.y))
こんな風に書ける気がしますが、コンパイラへの型指定を関数とみなすのも、それなりにいけるんじゃないかと思ったりしました。

書評: Lispプログラミング

Posted 2012-08-14 06:29:00 GMT

Lispプログラミング (中山隆/杉山武司)という本が1円だったので、ポチッてみた。
1986年出版でCLtL1は既に制定されている時期の本ではあるけど、どうかなと思いつつ買ってみたけど、やっぱり残念ながらCommon Lispの本ではなかった。
Common Lispの本だと思って買う人はいないと思うけど、折角なのでAmazonにレビュー書いてみた。
書いたレビュー:

1986年出版ということでCommon Lispは既に存在していましたが、本書で利用しているLispの処理系は、TLC-LispというMS-DOSで動く処理系で、ZetalispとStandard Lispを足したような方言のようです 80年代に沢山出版されたLisp&AIの本と同じく、基礎編でLispの基礎、応用でAIという定番の構成となっています。 TLC-Lisp自体はそれほどCommon Lispと違いはなく、説明もそれ程Common Lispに遠くありませんが、Common Lispで良い本は沢山ありますので、今となっては敢えて買う本ではないと思います。
ちなみに内容自体は、そんなに悪くないと思う。

書き捨てCLスクリプト (2012-08-12)

Posted 2012-08-11 21:30:00 GMT

やりたいこと

はてなダイアリーの記事の内容を、新しいブログの記事へのリンクへ差し替えたい
(defun hateg ()
  (load-time-value 
   (cxml:parse (kl:read-file-to-string "~/cadr.export.xml")
               (stp:make-builder))))

(let* ((doc (stp:copy (hateg))) ;破壊的に変更するのでコピー (cs (xpath:all-nodes (xpath:evaluate "/diary/day" doc))) ) (dolist (c cs) (let ((text (stp:string-value c))) (stp:delete-children c) (stp:append-child c (stp:append-child (stp:make-element "body") (stp:make-text (srfi-13:string-join (mapcar (lambda (x) (ppcre:register-groups-bind (date title) ("\\*(\\d+)*\\*(.*)" x) (format nil "*~A*~A~%~ この記事は<a href='http://g000001.cddddr.org/~A'>新しいブログ</a>に引っ越しました!" date title date ))) (ppcre:all-matches-as-strings "\\*\\d+\\*.*" text )) (string #\Newline) ))) ) )) ;; (print (xpath:all-nodes (xpath:evaluate "/diary/day[@date='2004-12-27']" doc))) (with-> "/home/mc/gg.xml" (stp:serialize doc (cxml:make-character-stream-sink >) )))

コード解説

  1. slime上でガチャガチャ作業。スクリプトを書いてシェルで実行するのではなくて、slime上で式を評価して実行
  2. 自作イメージを使っているので、cl-ppcre・kmrcl(kl)・xpath・cxml-stp・srfi・自作ユーティリティは、既にイメージ内にロードされている
  3. hateg: はてなダイアリーをxmlでエクスポートしてきたものを、stpに変換するもの。変数でも良いけど関数にしてみる。毎度評価される必要もないのでload-time-valueを使う
  4. cxml系の使い方はさっぱり忘れているので、逆引きCommon LispのXMLハンドリングの使えそうなところをコピペしたり
  5. xpathで、/diary/dayを集める
  6. 結果のリストを回す。dayの子要素は、bodyで、ここで回しているリストの要素がbody。これがエントリーの記事の本体なのでbodyの子要素を一旦削除し、リンクのテキストに差し替え。重複した表現やコンスしまくっているけど気にしない
  7. ;; 適当なノードで結果を確認したり
  8. with->: with-open-fileでオプション指定して出力するのが面倒なので自作して使っているマクロ。unixの作法と同じく基本上書きする
  9. stp:serializeでファイルにxmlとして書き出す
  10. できたので、はてなにインポートしてるけど、1400エントリー位あるためか、なかなか終らない
  11. 追記:どうも取り込みが遅いなあ、と思っていたらbodyノードを付け加えるのを忘れていたので修正

対話によるCommon Lisp入門 を読む

Posted 2012-08-11 13:08:00 GMT

思い付きでIRC(#lisp_scheme@freenode)で一人読書会を開催することにしてみました。
とりあえずのところ積読になっている本を片っ端から読んで行こうかなというところです。
とりあえず、薄目の本ということで、「対話によるCommon Lisp入門 」 を読むことにしました。以下、IRCのログをまとめたものです。

  • 突然ですが、暑いのと、ここが使われないのがもったいないので、lisp本を読む会を開催することにしました
  • といっても、私が本を読んで、独り言を投下するだけです
  • とりあえず 「対話によるCommon Lisp入門 」が手元で積読になっているので、これを読むことにします
  • まず、この本の解説ですが、出版は1993年。ANSI CLが制定される前ですが、ANSI準拠ということみたいです
  • 最初の版は絶版みたいですが、POD(print on demand)版として、現在でも入手可能です 「対話によるCommon Lisp入門 POD版」
  • 自分は、最初の版を460円で買いました。一時安かったのですが、現在若干高めですね

第I部 基礎的な対話 - 第1話 Lispを動かしてみよう

  • とりあえず、replを起動して色々打ち込んでみる、というところです
  • セットアップ等については省かれていますが、プログラミングの経験はあまり前提にしてはいないようですね

第I部 基礎的な対話 - 第2話 関数を定義してみよう

  • タイトルの通り関数の定義についての説明です。ifの説明のからみで、特殊形式について、ちょっとだけ触れられています。
  • ここまでのところ、束縛も代入も代入と表現されるみたいです。letで代入、という表現が若干気持ち悪かったりもしますが、letみたいな場合は束縛という表現が好まれる、という説明
  • 読み進めるうちに使い分けられるのかもしれません

第I部 基礎的な対話 - 第3話 プログラムやデータの構造を学ぼう

  • 評価規則や、関数呼び出し、関数の引数についての解説です。
  • 割と詳しく解説されているかなと思いました
  • この本は先生と生徒の対話形式で進んでいきますが、謎の語呂合わせのギャグ(まじめな人が言うようなギャグ)がやたら多いです
  • Scheme手習い と はじめての人のためのlisp を足して2で割ったようなところでしょうか
  • コラムが付いているのですが、この3話の終りには、Tachyon Common Lispの話が載っています

第I部 基礎的な対話 - 第4話 リスト処理の基本関数を理解しよう

  • タイトルの通り基本的なリスト処理の話
  • car・cdrは古いので、first・rest派のようです
  • 20年位前は、結構first・rest派がメジャーになりつつあったようなのですが、
  • いまでは、また普通にcar・cdrって感じですね。Schemeの標準ににfirst・restがないってのもあるでしょうか
  • コラムは0オリジンのメリットの話です

第I部 基礎的な対話 - 第5話 エディタで再帰的プログラムを作ろう

  • 再帰の説明ですが、結構盛り沢山です
  • factorialを手始めに、gcdや、nCkの組み合わせを求める関数等を説明します
  • 末尾再帰の説明とスタックオーバーフローの話もあります。エディタの使い方の話もちょっとあり
  • 若干時代を感じさせる記述もあります

第I部 基礎的な対話 - 第6話 再帰的なリスト処理を楽しもう

  • 全体的に結構末尾再帰の記述が多いみたいですね
  • これの兼ね合いでリストでの計算量の話もでてきますが、説明はスムーズに繋っている感じです
  • eq・eql・equalもここで説明されますが、equalが再帰的なので、
  • ここで説明となったのでしょう
  • 章末のコラムで評価有害説を取り上げていますが、evaluation considered harmful は初めて耳にしました
  • 評価結果とリダクション結果が対応しないのが良くないってことらしいですが、元論文があったら読んでみたいですね
  • quoteは、lispの設計ミスみたいな話は、リフレクションの3-lispあたりでも言われてた気はします

第I部 基礎的な対話 - 第7話 制御構造や変数を使い分けよう

  • cond・case・and・or等の制御構造の説明が主なところです
  • letと変数のスコープについての説明もあります。説明はかなり丁寧ですね
  • この話で第I部は終了のようですが、練習問題があるので、やってみます
  • ここまで読んでる限りでは、結構良い本ですね
  • 入門には良いんじゃないかと思いました。
  • なんというか、基礎知識的なところでは、ですが
  • 8話からは、第II部 発展的な対話 になります
  • こっからは、ANSI CLで盛り込まれた機能を使っていくらしいです
  • 第I部の練習問題やってみましたが、
  • learn/cl/introduction-to-common-lisp-in-dialogue/introduction-to-common-lisp-in-dialogue.lisp at master · g000001/learn · GitHub
  • 初歩のところで間違いましたw
  • やっぱり処理系に触らないで書くと全然書けないですね
  • define-symbol-macro 使ってるのは、コンパイラの警告を回避するためです(変数x yが未宣言となる)
  • テストには、fiveam使ってます
  • exptの問題も問題のいわんとするところを理解してなかったですねw

第II部 発展的な対話 - 第8話 関数の柔軟な仕様

  • 引数の&optional・&rest・&keyの説明と、多値の説明です
  • ここは普通な感じです
  • コラムにsetqと、set+quoteの比較がちょっとあったりします

第II部 発展的な対話 - 第9話 関数型プログラミング

  • 関数がファーストクラスであることを利用したプログラミングの解説、というところでした
  • スコープについても説明があり、ちょっとだけクロージャーとレキシカルスコープの説明があります
  • ここも割と丁寧に思いました

第II部 発展的な対話 - 第10話 命令型プログラミング

  • タイトルの通り命令型なプログラミング手法の解説です
  • 主に副作用を目的に使うsetf・incf等の〜f系の説明があります
  • 加えて、loopマクロの説明が結構あるのですが、
  • loopの構文がansi clのloopでないので残念ですねー
  • (defun gcd! (m n)
      (loop :collect (list m n) :into seq
            :until (= (mod m n) 0)
            :do (psetf m n
                       n (mod m n))
            :finally :return (values n seq)))
  • みたいなのが通るようなのですが、これはansiでは無理なんですね
  • (defun gcd! (m n)
      (loop :collect (list m n) :into seq
            :until (= (mod m n) 0)
            :do (psetf m n
                       n (mod m n))
            :finally (return (values n seq))))
  • のように、finally return exp というのは、finally (return exp)にする必要はあります
  • このloopは、PAIPに載ってるloopなんでしょうか
  • 10.34で、finallyの直後が(return ...)のとき、それを囲むかっこは省略できます というのがANSIでは省略できないってところですね
  • 章末のコラムにマクロの話が出てきますが、どういう時に必要になるかという話だけのようです
  • どうもこの本はマクロの定義の解説はないみたいですね

第II部 発展的な対話 - 第11話 表とデータ抽象

  • シンボルの属性リストから始まって、ハッシュ、構造体等、データ構造の説明です
  • 各々の長所短所の説明や、循環構造の説明もあります
  • コラムはリストを使わないで、cons・car・cdrを定義する話

第II部 発展的な対話 - 第12話 オブジェクト指向プログラミング

  • CLOSの解説ですが、最初の説明でクラスではなくて構造体を使っているのが面白いところです
  • 構造体だと継承が思ったようにいかない例を挙げて次にdefclassに切り換えます
  • 次に多重継承(mixin)の説明
  • eql特定子の話もちょっとだけ出てきます
  • 割と良くまとまっているかなと思いました

第II部 発展的な対話 - 第13話 Lispの内部構造とインタプリタ

  • CLOSを使って、lisp2なインタプリタを作るという結構面白い内容です。
  • closの練習にもなるしなかなか良いですね
  • II部にも練習問題があるのでやってみます
  • learn/cl/introduction-to-common-lisp-in-dialogue/introduction-to-common-lisp-in-dialogue.lisp at master · g000001/learn · GitHub
  • 自分は上の解答みたいな感じになりましたが、本の解答のunionは破壊的変更ありなんですね
  • 11〜13話で補足ですが
  • #(NIL NIL NIL NIL)
  • ってところは、ANSI CLでは処理系依存です
  • loopのfinally returnもansi clの規格外なのですが、CLtL2では、この形式がアリというのを教えてもらいました
  • この二者が動くのはCLISPなので、この本で使ってる処理系はCLISPなのかもしれないですね
  • それと、12話の最後に 日本LISP協議会 という組織が紹介されていて、
  • 年2000円で、ニュースレターなどが配布されていたようです
  • 住所からすると、どうも日本シンボリックス株式会社/ニチメングラフィックス株式会社の主催みたいですね
  • 参考リンク: SFX BROTHERS Symbolics 007

まとめ

  • とりあえず、ざっと読んでみましたが、入門には結構良い本ですね
  • マクロ等取り上げられていないトピックスもありますが、他の書籍が充実しているので他の本を読めばOKだと思います
  • loopや、closが取り上げられているってのも、なかなか良いところです
  • 対話によるCommon Lisp入門→ポール・グレアムのANSI Common Lispって流れも良いかもしれないですね

Older entries (1492 remaining)