よろずや

comp.lang.lisp メモ

google のサービスを使ってブラウザだけで comp.lang.lisp が読めます.自分の理 解に沿って紹介してますので,ちゃんと読みたい方は原文を参照してください. Message-ID をクリックすれば原文が読めます.誤解してるぞ!! というのは指摘して いただけるとありがたいです.

[ google 経由で comp.lang.lisp を読む ]

目次

Some Macro Questions

マクロに関する質問から始まるスレッド.みんな親切に解説してくれてる.

examples of non-traditional source code presentation?

Lisp コードをかっこよくプレゼンしたいという話.

http://www.drscheme.org/tour/tour-Z-H-13.html http://www.drscheme.org/tour/tour-Z-H-5.html#node_chap_4 http://www.emacswiki.org/cgi-bin/wiki/PrettyLambda http://www.emacswiki.org/cgi-bin/wiki/UnParenMode http://www.emacswiki.org/cgi-bin/wiki/DimParentheses http://clocc.sourceforge.net/ の YTools http://www-spi.lip6.fr/~queinnec/WWW/LiSP2TeX.html http://www.ccs.neu.edu/home/dorai/slatex/

とかが出てきた.

Explanation of macros; Haskell macros

マクロの話だが,

You can find a short comparison of Scheme and Template Haskell (TH) approach in (pages 12-13): http://www.haskell.org/th/papers/meta-haskell.ps

こんなのが紹介されていた.

Why don't people like lisp?

BIG successes of Lisp (was ...) の派生スレッド.

(do ((a 1 b) (b 1 (+ a b))) 
     (nil a) 
     (print a))

とかどーだ?

a, b = 1, 1
while 1:
   print a
   a, b = b, (a + b)

でいいじゃん.

(loop
   for a = 1 then b
   and b = 1 then (+ a b)
   do (print a))

なら Python 版より 30 倍速くてわかりやすいぞ,

(defmacro recur (vars inits next termination result &body body)
   `(do ,(loop for tail on vars
               for init in inits
           collect (list (first tail) init
                         (if (rest tail)
                         (second tail)
                         next)))
        (,termination ,result)
         . ,body))

を使えば

(recur (a b) (1 1) (+ a b) nil a (print a))

でいいし,階乗計算とかも

(recur (a) (1) (* 2 a) nil a (print a))

ででるきじゃねーか.

loop なんか lisp に見えねー.

等々.他のツリーでも,comp.lang.lisp と comp.lang.python へのポストなのに Haskell や Ruby が出てきたりしてるのでその手の言語論争が好きな人はどうぞ.

My take on ARC

ARC についての Erann Gat の見解から始まるスレッド.

どうやら Paul はミニマリズムに夢中で言語のコアを小さいままにしておきたいらしい. 基本データ型をできるかぎり少なく,できれば文字列や数値まで全部リストで…過激な.

というわけで ARC に興味のある人は必見.

Homework question: LOOP

↓を再帰で書いたのと同等の効率で,よりわかりやすく LOOP を使うようにしろっていうんだけど?

(defun prior-sib-if (self list &optional (test-fn #'true-that))
   "Find nearest preceding sibling passing TEST-FN"
   (labels ((check-priors (sibs)
              (if (eql self (first sibs))
                  nil
                (or (check-priors (rest sibs))
                  (when (funcall test-fn (first sibs))
                    (first sibs))))))
     (check-priors list)))

というネタ.

(defun prior-sib-if (self list &optional (test-fn #'true-that))
  "Find nearest preceding sibling passing TEST-FN"
  (loop with sib = nil
        for node in list
        when (eql node self) return sib
        when (funcall test-fn node) do (setf sib node)))

とか

(defun prior-sib-if2 (self list &optional (test-fn #'true-that))
  "Find nearest preceding sibling passing TEST-FN"
  (loop for a in
        (loop for b in list and l = (cons b l)
              do (when (eql self b) (return l)))
        do (when (funcall test-fn a) (return a))))

とか.

(defun prior-sib-if (self list &optional (test-fn #'true-that))
  "Find nearest preceding sibling passing TEST-FN"
  (loop with candidates = nil
      for node in list
          until (eql node self) do (push node candidates)
          finally (return (loop for c in candidates when (funcall test-fn c) return c))))
(defun prior-sib-if (self list test-fn)
  (loop with result = nil
     for x in list until (eql x self)
     do (when (funcall test-fn x)
          (setf result x))
     finally (return result)))

;; This is another approach your teacher might not like:
(find-if test-fn list :end (position self list) :from-end t)

とか色々あるので詳細はスレッドを追ってください.

defmethod vs defun

コンパイル時に引数の型がわかっている関数に対して,defun の代わりに defmethod を使っていいの?

(defmethod is-string ((thestring string)) (stringp thestring))
(defun is-string (thestring) (stringp thestring))

というネタ.

(defmethod is-string (var) nil)
(defmethod is-string ((var string)) t)

でいいじゃんとか.使いわけについて語ってくれる人が居たりとか.

Papers/essays on lisp compiler construction

誰か Lisp コンパイラ作成技法(dragon-book みたいなやつ)へのリファレンスを教 えて〜パース/コンパイルだけじゃなくて,最適化パスもあると良いな〜とのこと.

いろいろ紹介されてる.

What you can do with macros in Lisp

コンパイル時にジャンプテーブルに展開されるような NCASE をマクロで…という話.

ILC 2003: some impressions

ILC 2003 の感想.

Christian Queinnec (http://www-spi.lip6.fr/~queinnec/WWW/Queinnec.html) - 継続のチュートリアル Jerry Sussman's (http://www.swiss.ai.mit.edu/~gjs/gjs.html) - 基調講演 Gregor Kiczale's (of MOP fame) (http://www.cs.ubc.ca/~gregor/) - Aspect Oriented Programming (AOP) のプレゼンテーション Espren Verste - 実際に Lisp が使われてる話.LispWorks で Linux/Windows/Mac 対応の GUI アプリを作って日常的に使ってるよとか. Antonio Leit縊 (http://www.evaluator.pt) - Linj という Lisp から Java へのコンパイラのプレゼンテーション.

McCarthy 先生ネタで盛りあがってたり,いろんな人の感想でたり.

BIG successes of Lisp (was ...)

Lisp の成功って少なくとも次の 3 つがあると思うけど,

a. orbitz.com web site uses Lisp for algorithms, etc. b. Yahoo store was originally written in Lisp. c. Emacs

a. orbitz.com ってメンテンナンスのために結構頻繁にシャットダウンしてるよね? b. Yahoo store って結局 Lisp じゃない言語に書き換えられちゃったよね? c. Emacs って遅くでデカイので有名だよね? Common Lisp で書かれてるわけじゃないけど…

ViaWeb や Orbitz は LaTeX よりも成功したと言える? Lisp マシンみたいな失敗もあったし.みたいなネタだったんだけど, LaTeX がらみの話は cl-pdf や cl-typesetting へ.

本題は

Macsyma and Maxima http://maxima.sourceforge.net/

Powercase and Watson http://www.xanalys.com/solutions/powercase.html

AutoCAD http://www.autocad.com

とかもあるじゃん,とか.

(newsp ARC)?

ILC 2003 の Paul Graham のキーノート講演聞く人,内容報告して〜という話.

残念ながら,それほど ARC に関する目新しいネタはなかったみたい.The Road to Lisp Survey に Paul Graham のページ が できたみたい.Interlisp 使いだったとは知らなかった.

cmucl/multiprocessing

cmucl 18e で multiprocessing を使うと,

となってしまいます.ひょっとして壊れているの?というネタ.

 #+mp (mp::startup-idle-and-top-level-loops)

を ~/.cmucl-init file に書いておいたほう良いというフォローが詳しい解説つきで.

start-sigalrm-yield についての話も出てくる.

Counting occurences of an item in a list: is there a better way?

(let ((result 0))
   (loop for i in finals
         do (when (= i h)
              (incf result)))
   result))

のようにリスト中の数字の出現回数を数えたいんだけど CL に組み込みであったりする?というネタ.

(count h finals :test #'=)
(loop for i in finals counting (= i h))

複数の数字を数えたいなら,

  (loop for i in finals
    counting (= i h1) into h1s
    counting (= i h2) into h2s
    finally (return (list h1s h2s)))

再帰で書いたら,

(labels ((r-count (list count)
           (if list
               (r-count (cdr list) (if (= (car list) test-value) 
                                       (1+ count) 
                                       count))
               count)))
   (r-count <the-list> 0))

とかだんたん不明瞭さを競い始めて終り.

setf problem

>> (setf z '(a b c))
>> (defun nl (i lis) (nth i lis))

を定義して,

>> (setf (nth 1 z) 'd)

としたら,

z = (a d c)

となって欲しいのに nl が定義されてない,となるのはどうして?

というように,要するに setf マクロに関する質問.

(defun (setf nl) (new-value i list) (setf (nth i list) new-value))

あるいは,

http://www.lispworks.com/reference/HyperSpec/Body/m_defset.htm http://www.lispworks.com/reference/HyperSpec/Body/m_defi_3.htm

を参照すれば良いとのフォローが.

CL & Practicality

CL は Scheme より実用的って聞くけど,なんで?というような話から始まる長いスレッド.

[ANN] pg-dot-lisp version 0.19

PostgreSQL にアクセスするためのパッケージ.CMUCL, SBCL, CLISP, OpenMCL, Allegro CL, Lispworks, MCL and ArmedBear Lisp で動作する.

http://www.chez.com/emarsden/downloads/

から入手可能だそうで.

ArmedBear Lisp ってのは↓ http://armedbear-j.sourceforge.net/

Is it just style or another reason?

"On Lisp" (sect. 4.1, pg 42) に載ってる例で,

(defun find2 (fn lst)
  (if (null lst)
      nil
    (let ((val (funcall fn (car lst))))
      (if val
   (values (car lst) val)
 (find2 fn (cdr lst))))))

で,なんで (car lst) を二回も使ってるのか?↓のようにしないのはスタイルなの か,何か理由があるのか?

(defun find3 (fn lst)
  (if (null lst)
      nil
    (let* ((x (car lst))
    (val (funcall fn x)))
      (if val
   (values x val)
 (find2 fn (cdr lst))))))

という質問.

car はコードを読みにくくまでして回避するほど重くはないとか,変数バインドする if を使ったらどうかとかフォローが付く.

さらに,

Message-ID: <cf333042.0309300847.70cec19b%40posting.google.com>

では,

なんでレキシカル変数へのアクセスが CAR より軽いと思うのか?レキシカル変数 は一般的にスタックフレームやクロージャに保持され,アクセスにはスタックフレー ムやクロージャへのポインタにインデックスを使った間接参照が必要になる.そこ にはレジスタにキャッシュするような最適化がない.CAR は CONS の CAR フィー ルドへのポインタを経由したアクセスであり,同じことではないか.

最適化コンパイラは CSE 最適化(共通式の除去)を行うことができるかもしれない.それは,つま り (CAR LST) の値が一回目にレジスタにキャッシュされ,次回以降はそれを使う ことになる.この最適化が可能であることを発見するのは容易なことである.なぜ なら,LST はレキシカル変数であり,レキシカルスコープ内で代入されておらず, また CAR は副作用の無い標準関数であるから.

といった内容のフォローが.で,これに対して,

レキシカル変数ってレジスタに置かれるでしょ?この場合 funcall が lst を変更 しない事がわからないので CSE は無理でしょ?レキシカル変数が変更されないのを 検出するほうが簡単.

といったフォローが.

Documentation strings in defclass

(defclass myclass () () (:documentation "doc string"))
(defvar client (make-instance 'myclass))

のドキュメントストリングの読み方がわからないという話.

(documentation (find-class 'myclass) t)
(documentation 'myclass 'type)
(documentation (class-of client) t)

とかで読める.

concatenate atoms into string

こんなのってある?という話.

(defun join (wlist sepchr)
  (let ((string nil))
    (dolist (sym wlist string)
      (setf string
        (concatenate 'string
          string (if string sepchr nil) (string sym))))))

;; >(join '("happy" "new" "year") "-")
;; >"happy-new-year"

format や loop や reduce を使った実装がフォローされる.

announcing Chio, a new string processing library

文字列処理ライブラリ Chio のアナウンス.

http://www.mathphysics.com/spingarn/chio/ http://www.mathphysics.com/spingarn/chio/examples/index.html

cl-typesetting news

cl-typesettings に関するニュース

New features:

サンプル

http://www.fractalconcept.com/ex.pdf

Lisp コード

(in-package typeset)

(defparameter *par1* "Lisp is a family of languages ...")

(defparameter *par2* "MacLisp improved...")

(defparameter *par3* "Interlisp introduced many...")

(defparameter *par4* "Although the first implementations...")

(defparameter *par5* "The Lisp machine concept...")

(defun draw-block (content x y dx dy rotation)
  (pdf:with-saved-state
      (pdf:translate x y)
    (pdf:rotate rotation)
    (pdf:set-gray-fill 1)
    (pdf:basic-rect -5 0 (+ dx 10) (- dy))
    (pdf:fill-and-stroke)
    (pdf:set-gray-fill 0)
    (let ((box (make-filled-vbox content dx dy)))
      (push box *boxes*)
      (stroke box 0 0))))

;; example of extension

(defclass rotated-char-box (soft-box h-mode-mixin)
  ((boxed-char :accessor boxed-char :initarg :boxed-char)
   (rotation :accessor rotation :initarg :rotation)))

(defun put-rotated-char-string (string)
  (loop for char across string
 do (add-box (make-instance 'rotated-char-box :dx *font-size*
       :dy *font-size* :boxed-char char
       :rotation (- (random 120) 60)))
 (incf *char-rotation* *char-rotation-inc*)))

(defmethod stroke ((box rotated-char-box) x y)
  (let ((dx (dx box))(dy (dy box))
 (width (pdf:get-char-width (boxed-char box) *font* *font-size*)))
    (pdf:with-saved-state
      (pdf:translate (+ x (* dx 0.5))(- y (* dy 0.8)))
      (pdf:circle 0 0 (* dx 0.45))
      (pdf:stroke)
      (pdf:rotate (rotation box))
      (pdf:in-text-mode
       (pdf:move-text (* -0.5 width)(* -0.18 *font-size*))
       (pdf:set-font *font* *font-size*)
       (pdf::show-text (make-string 1 :initial-element (boxed-char box)))))))

;;example document

(defun ex (&optional (file #P"/tmp/ex.pdf"))
  (pdf:with-document ()
    (pdf:with-page ()
      (pdf:with-outline-level ("Example" (pdf:register-page-reference))
 (pdf:set-line-width 0.1)
 (let ((content
        (compile-text ()
   (paragraph (:h-align :centered)
       (with-style (:font "Helvetica-Bold" :font-size 30 :color '(0.0 0 0.8))
         "cl-typesetting") :eol
       (with-style (:font "Times-Italic" :font-size 13)
         "The cool Common Lisp typesetting system"))
   (paragraph (:h-align :justified :top-margin 10 :first-line-indent 10)
       (with-style (:font "Times-Italic" :font-size 10)
         "This typesetting system's goal is to be an alternative to the TeX
typesetting system. It is written in Common Lisp and uses cl-pdf as its
backend. This will enable it to be powerful, extensible and fast. Though not
considered very difficult, it is already better than Word..."))
   (paragraph (:h-align :centered)
       (with-style (:font "Helvetica-BoldOblique" :font-size 20 :color '(1.0
0 0))
         "Now in Color!"))
   (paragraph (:h-align :centered)
       (with-style (:font "Times-Italic" :font-size 12 :color '(0.0 0.3 0.1))
         "And with user defined "
         (put-rotated-char-string "extensions")))
   (paragraph (:h-align :justified :top-margin 5 :first-line-indent 10 :color
'(0 0 0))
       (with-style (:font "Times-Roman" :font-size 10)
         *par1*))
   (paragraph (:h-align :left :top-margin 9 :first-line-indent 10
:left-margin 10)
       (with-style (:font "Helvetica" :font-size 10)
         *par2*))
   (paragraph (:h-align :centered :top-margin 9)
       (with-style (:font "Helvetica" :font-size 10)
         *par3*))
   (paragraph (:h-align :justified :top-margin 9 :first-line-indent 10)
       (with-style (:font "Helvetica-Oblique" :font-size 10)
         *par4*))
   (paragraph (:h-align :justified :top-margin 9 :first-line-indent 10)
       (with-style (:font "Helvetica-Oblique" :font-size 10)
         *par5*))
   (paragraph (:h-align :centered :top-margin 20)
       (with-style (:font "Times-Bold" :font-size 20)
         "Kerning test") :eol
       (with-style (:font "Helvetica" :font-size 40 :first-line-indent 10)
         "Yes, AWAY"))
   (paragraph (:h-align :centered :top-margin 20)
       (with-style (:font "Helvetica" :font-size 40 :color '(0.8 0 0))
         "Warning!") :eol
       (with-style (:font "Times-Italic" :font-size 12)
         "This test pdf file has been made with an early version 0.1 of
cl-typesetting. A lot of basic features, like a correct hypenation, are still
missing, though micro-typography is already there")))))
   (draw-block content 40 800 250 380 5)
   (draw-block content 50 425 250 380 -5)
   (draw-block content 330 800 250 380 -2)
   (draw-block content 310 400 250 380 2))))
    (pdf:write-document file)))

これも様々な意見が出て長〜いスレッドに.

Macroexpansion question

二通りに展開できるマクロがあるとき,マクロが位置する lexical context に従っ て展開を選びたい.

(with-context-1 ... (my-macro) ...) ==> (... expansion-1 ...)
(with-context-2 ... (my-macro) ...) ==> (... expansion-2 ...)

完全な code waler なしでこれを行なう方法ってある?

…というネタ.

Message-ID: <my-first-name.my-last-name-2409031533200001%40k-137-79-50-101.jpl.nasa.gov>

あー気にしないで.思いついた.

(defmacro with-context (context &body body)
  (let ( (*context* context) )
    `(funcall ',(compile nil (lambda () ,@body)))))

(The compiler *is* a code-walker.  Duh!)

これって Scheme でできる? :-)

Message-ID: <R-Cdned1bP-Nt--iU-KYvA%40dls.net>

(with-context-1 ...) ==>
    (macrolet ((context-indicator () 'context-1))
       ...)
(with-context-2 ...) ==>
    (macrolet ((context-indicator () 'context-2))
       ...)

(defmacro my-macro (&environment env)
    (ecase (macroexpand '(context-indicator) env)
      (context-1 ...)
      (context-2 ...)))

以下,これって正しいの?とか &environment って他に使い道あるのか?とか.興味 があるひとはスレッドを読んでみてください.

Ex. 3.5 in ACL

Paul Graham の ANSI Common Lisp の例題 3.5 に挑戦してます.

   define a function pos+, that takes a list as param and returns a list 
   that adds the position of each element of the list to the element's value.
   Thus:
     (pos+ '(7 5 1 4))
   returns:
     (7 6 3 7)

という問題なんですが,再帰で解くのは簡単でした.しかし,反復と mapcar を用い て定義しなけりゃなりません.私の解法は以下のとおりですが,汚くてセンスのない 副作用を使っており,見た目もエレガントじゃありません.なにかもっとよい方法は ないでしょーか?

mapcar:

(defun pos+1 (lst)
   (setf i 0)
   (mapcar #'(lambda (x) (let ((new (+ x i)))
                           (progn (setf i (+ i 1))
                             new)))
           lst))

iteration:

(defun pos+ (lst)
   (setf acc NIL)
   (setf i 0)
   (dolist (obj lst)
; i know, instead of append, i could do a cons and reverse afterwards...
     (progn (setf acc (append acc (list (+ obj i))))
       (setf i (+ i 1))))
   acc)

というネタ.

Message-ID: <slrnbn3c83.ibt.xach%40unnamed.xach.com>

mapcar の方は let や incf を使ったほうが良いとおもう.

   (defun mapcar-pos+ (list)
     (let ((i -1))
       (mapcar #'(lambda (elt) (+ elt (incf i)))
	       list)))

反復のほうは,私の好みは LOOP を使ったこれ.

   (defun loop-pos+ (list)
     (loop for i from 0
	   for elt in list
	   collect (+ elt i)))

しかし DO を使っても簡単にできる.

   (defun do-pos+ (orig-list)
     (do ((i 0 (1+ i))
	  (list orig-list (cdr list))
	  (new-list nil (cons (+ (car list) i) new-list)))
	 ((endp list) (nreverse new-list))))

↓のコメントが有用だと思う.

http://www.cs.northwestern.edu/academics/courses/325/readings/graham/graham-notes.html

あと,PAIP も良いよね,という話.

Message-ID: <m3ad8uf9hp.fsf%40buss-14250.user.cis.dfn.de>

(mapcar (let ((i -1)) (lambda (elt) (+ elt (incf i)))) list)

Message-ID: <3f71b5c8%240%2420625%24626a54ce%40news.free.fr>

(defun pos+ (list)
  (let ((i 0))
    (mapcar #'(lambda (x)
                (prog1 (+ x i) (incf i)))
            list)))

などなど.

running the clx example hello.lisp in cmucl

    CMU Common Lisp CVS release-18e-branch + minimal debian patches, running on tinyWith core: /usr/lib/cmucl/lisp.core
    Dumped on: Wed, 2003-09-24 10:55:31+01:00 on tiny
    For support see http://www.cons.org/cmucl/support.html Send bug reports to the debian BTS.
    or to pvaneynd@debian.org
    type (help) for help, (quit) to exit, and (demo) to see the demos
     
    Loaded subsystems:
        Python 1.1, target Intel x86
        CLOS 18e (based on PCL September 16 92 PCL (f))
    * (load "hello")
     
    ; Loading #p"/usr/share/common-lisp/source/cmucl-clx/demo/hello.lisp".
    T
    * (hello-world "localhost")
    ;
     
    ; Warning: This function is undefined:
    ;   HELLO-WORLD
     
    Error in KERNEL:%COERCE-TO-FUNCTION:  the function HELLO-WORLD is undefined.
     
    Restarts:
      0: [ABORT] Return to Top-Level.
     
    Debug  (type H for help)
     
    (KERNEL:%COERCE-TO-FUNCTION HELLO-WORLD)
    Source:
    ; File: target:code/fdefinition.lisp
    (ERROR 'UNDEFINED-FUNCTION :NAME NAME)

が動かない.(xlib:hello-world "localhost") としてもダメ.なんで?という話.

(xlib::hello-world "localhost")

(in-package :xlib)
(hello-world "localhost")

でどう?という話で本題は終了.

Message-ID: <8665jfg4ph.fsf%40cawtech.freeserve.co.uk>

CLX ってとっかかりが難しいよね.ということで,

http://www.cawtech.demon.co.uk/examples.txt http://www.cawtech.demon.co.uk/source.tar

を書いてみました.という話.これは初心者には良いかも.

cl-typesetting first tests

cl-typesetting のファーストテスト.↓を見てくれ,長い.

http://www.fractalconcept.com/ex.pdf

という話から始まるスレッド.長い.

newbie: nice function like princ

こんな関数を作ってみました.

(DEFUN PRINCC (&rest params)
    (DOLIST (sym params)
        (IF (EQ sym T) (TERPRI) (PRINC sym))))

小さくて使うのも簡単.複数の引数を受けとれるので,引数の中で format を使うこともできます.

(princc "The sum of 2*8 is " (* 2 8) "." T)
(princc "The sum of 2*8 is: " T (format nil "~A~A~%" (* 2 8) "."))
(princc T "This function permits to use more arguments." T T)
(princc T "This function permits to " (prin1-to-string "use") " more arguments." T T)

どうでしょう?というネタ.

Message-ID: <m33cenxei1.fsf%40jcooper02.sagepub.com>

改行するのに t じゃなくて nil を使うのを気にしなれば,こーゆうのでもできる.

(format t "~@{~:[~%~;~:*~a~]~}" "This function permits to " (prin1-to-string "use") 
        " more arguments." nil nil)

Message-ID: <NDmcb.344901%24lK4.11023676%40twister1.libero.it>

format がこんなにパワフルだなんて知らなかった!!

で,本題は終るのだが,この後,

[ Message-ID: <7c1401ca.0309252228.1862fe1d%40posting.google.com> ]

でFORMAT ってチューリング完全だっけ?という話が出て,

http://99-bottles-of-beer.ls-la.net/c.html#Common-Lisp-(format-string)

が紹介されたり

[ Message-ID: <oex7puoj.fsf%40ccs.neu.edu> ]

I remember that FORMAT in CLtL 1 was just shy of being turing complete, but I haven't bothered to try to prove it for CLtL 2.

The FORMAT string itself allows for a very complicated state machine, but unless you have something isomorphic to a writable `tape', you won't be turing complete. (Another approach would be to use ~? to reflect your state. This might allow you to use the `stack' of pending formats as a tape, but I'm not sure.)

という話がでたり,脱線気味に漏りあがっていた.

getting started...

Message-ID: <bkpiae%24gok%241%40online.de>

最近 lisp に興味を持ち始めて,paul graham の ANSI Common Lisp を買ってきまし た.しかし,どうやらとっかかりに基本的な情報が足りないようです.clisp + ilisp に関するヒントを見つけて debian-package でインストールしましたが,

1) どうやって clisp を起動するのでしょうか? M-x clisp-hs ?(でも,それをす ると常に -a option is deprecated というエラーメッセージが出るんですが)

2) どのようにデバッガを使えばいいですか?(とりあえず abort と backtrace が あればとっかかりには十分なのですが…)

3) 何を .emacs に追加すれば良いのでしょうか? 理解できないような沢山の異なる 設定ではなくsyntax colouring といった本当に基本的な事だけでいいのです.

4) lisp-mode での if 式が次のようにフォーマットされます

(if ...
     (some code)
   (some other code))

という質問.そういえば,その辺は ANSI Common Lisp には載ってない.処理系や環 境毎にまちまちなので本には向かないトピックだからかなぁ….

Message-ID: <877k3z61vc.fsf%40bird.agharta.de>

clisp + ilisp は良い選択です.もし x86 の環境なら CMUCL や SBCL もチェックし ましょう.PPC なら OpenMCL を見てみましょう.それらは全て clisp よりも ANSI 規格に準拠しており,ネイティブコードにコンパイルできます.

1) どうやって clisp を起動するのでしょうか? M-x clisp-hs ?(でも,それをす ると常に -a option is deprecated というエラーメッセージが出るんですが)

このメッセージは無視してかまいません.もしそのエラーメッセージを消したないら ば,emacs の変数 "clisp-hs-program" をチェックしてください.おそらくそれが原 因です.私は

(setq clisp-hs-program "/usr/local/bin/clisp -ansi -I -K full -Efile ISO-8859-1 -i ~/.clisp.lisp")

と自分の ~/.ilisp に書いています.(-a のかわりに -ansi を使っている点に注意)

2) どのようにデバッガを使えばいいですか?(とりあえず abort と backtrace が あればとっかかりには十分なのですが…)

CLISP のデバッガを起動しているなら, "?" をタイプすることでコマンド一覧が表 示されます.これ(あるいは help や :help)はほとんどの Common Lisp の実装で 動くとおもいますが,しかしデバッガのコマンドは実装依存な点に注意.

あるいは ILISP のデバッガコマンド群を使うこともできます.それはそれぞれの実 装毎に異なるデバッガのコマンドを覚えておかなくてもいいですが,最小公倍数的な 機能しかありません.これらのコマンドを見つける持っとも簡単な方法は,ILISP メ ニューから探すことです.

3) 何を .emacs に追加すれば良いのでしょうか? 理解できないような沢山の異なる 設定ではなくsyntax colouring といった本当に基本的な事だけでいいのです.

syntax coloring はただ

(global-font-lock-mode 1)

をあなたの ~/.emacs に追加するだけです.(あたなが 非常に 古くて遅いマシンを使ってなければ)

4) lisp-mode での if 式が次のようにフォーマットされます

This is how Emacs thinks IF forms should be intended because their semantics in Emacs Lisp is different from how they work in Common Lisp. Add something like

(add-hook 'lisp-mode-hook (lambda () (set (make-local-variable lisp-indent-function) 'common-lisp-indent-function)))

to your ~/.emacs file to get better indentation.

You should also make sure that you have the CLHS and CLtL2 available from within ILISP. I think this can be done conveniently with apt-get - I'm not a Debian user.

Have fun, Edi.

Question about scoping

Message-ID: <pan.2003.09.10.10.51.17.422000%40hotmail.com>

昔,E. (← Erann Gat の事)が Scheme は静的スコープのみなので CL よりも Algol に近い,というような事を言ってたような気がする.それの意味するところが 十分わかってないので動的スコープの有利な点を簡単に説明してちょーだい.というネタ.

Message-ID: <874qzkap14.fsf%40bird.agharta.de>

  * (defun foo (x) (format t "Result: ~A" (* x x)))

  FOO
  * (with-output-to-string (*standard-output*) (foo 3))

  "Result: 9"

foo は ∗standard-output∗ に出力するだけのシンプルな関数だけど, ∗standard-output∗ がダイナミックスコープなので簡単に foo の出力を別のストリー ムに "リダイレクト" できる.

参照

Message-ID: <c366f098.0309100525.7c8edc6d%40posting.google.com>

API の実装に有用かどうか,という質問では?ということで,「ストリーム出力用の APIの実装」という問題に対する Common Lisp の Solution を解説.

Message-ID: <kw65k0lq5o.fsf%40merced.netfonds.no>

Message-ID: <ullsvv4fx.fsf%40terra.es>

CL-USER 7 > (with-output-to-string (*standard-output*) (room nil))
"
Total Size 81702K, Allocated 44499K, Free 37041K
"

Message-ID: <bjn8fr%24tha%241%40f1node01.rhrz.uni-bonn.de>

Scheme と CL はそれぞれ別のコミュニティがある.Schemer は minimal な言語を望 み,また dynamic scoping は lexical scoping で簡単にシミュレートできるのでそ れを言語に含めるなんて納得できない.completeness を目指す言語では dynamic scoping を言語に含めるのは合理的.

とか,あとは

lexical scoping のほうが 一般的に 良いわけではない.ものによっては dynamic scoping のほうが良いデザインになる.

とか.

http://progwww.vub.ac.be/~wdmeuter/PostJava/Costanza.pdf - Dynamically Scoped Functions as the Essence of AOP

このスレッドはまだ続くのですが,長いので興味のある人は Question about scoping をどーぞ.

ANN: Road to Lisp is now the Road to ALU

Message-ID: <877k459z2y.fsf%40noetbook.telent.net>

Road to Lisp Survey を alu.cliki.net に移動させたよ,というアナウンス.

http://alu.cliki.net/The%20Road%20to%20Lisp%20Survey

Hash table probe...

Message-ID: <3F6B370A.5000102%40mac.com>

(stash-uniquely key value hash
                 (error "Frobnitz catastrophe, duplicate ~S (value ~S) 
(existing value ~S)" key value item))
(defmacro stash-uniquely (tag value hash-table error-expr)
           (with-gensyms (found? tag-value value-value hash-table-value)
               `(let ((,tag-value ,tag)
                      (,value-value ,value)
                      (,hash-table-value ,hash-table))
                     (multiple-value-bind (item ,found?)
                              (gethash ,tag-value ,hash-table-value)
                         (if ,found? ,error-expr)
                         (setf (gethash ,tag-value ,hash-table-value)
                               ,value-value)))))

で declare ignorable を使ったほうが良いとか,item が error-expr にだけ参照さ れるようにしたほうが良いとか.

with-gensyms は On Lisp で紹介されているマクロ. clocc にも入ってる.

うーん,↓くらいしか考えつかないなぁ….item が error-expr にだけ参照される ようにするってのはどーすりゃいいのかなぁ.item も with-gensyms で作って, error-expr を関数でラップ,引数として item を渡す,とか?

(defmacro stash-uniquely2 (tag value hash-table error-expr)
  (with-gensyms (found? tag-value value-value hash-table-value)
    `(let ((,tag-value ,tag)
           (,value-value ,value)
           (,hash-table-value ,hash-table))
       (multiple-value-bind (item ,found?)
           (gethash ,tag-value ,hash-table-value)
         (declare (ignorable item))
         (if ,found? ,error-expr)
         (setf (gethash ,tag-value ,hash-table-value)
                ,value-value)))))

(defmacro stash-uniquely3 (key value hash-table error-expr)
  (pg:with-gensyms (found? key-value value-value hash-table-value item)
    `(let ((,key-value ,key)
	   (,value-value ,value)
	   (,hash-table-value ,hash-table))
      (multiple-value-bind (,item ,found?)
	  (gethash ,key-value ,hash-table-value)
	(if ,found? (funcall (lambda (item) (declare (ignorable item)),error-expr) ,item))
	(setf (gethash ,key-value ,hash-table-value)
	      ,value-value)))))

List comprehensions for Lisp version 2

Message-ID: <m3brtf2ln0.fsf%40h137n2fls305o851.telia.com>

list comprehensions の Common Lisp による実装.ドキュメントやソースが http://www.csd.uu.se/~svenolof/Collect から入手できる.

why does this ecase form not work?

Message-ID: <bkaq0j%24bf8%241%40mughi.cs.ubc.ca>

(ecase "a"
   ("a" 4)
   ("b" 5))

が動かないのはなんで?その後,比較 eql 使ってるからなのはわかったけど,equal 使うにはどーしたらいい?とのフォローが.

Message-ID: <3F68F2CE.4060101%40nyc.rr.com>

(cdr (assoc "a" '(("a" . 4)("b" . 5)) :test 'string-equal))

とか

(defmacro case$ (keyform &rest clauses)
   (let ((key$ (gensym)))
       `(let ((,key$ ,keyform))
            (cond
              ,@(mapcar (lambda (clause)
                           (destructuring-bind (match$ &body code) clause
                              `((string-equal ,key$ ,match$) ,@code))))))

とか.ただし,マクロのほうは t や otherwise を扱えないけど.

Message-ID: <lzu17alqhd.fsf%40unity.copyleft.no>

Message-ID: <lz1xuel8k0.fsf%40unity.copyleft.no>

http://groups.google.com/groups?safe=images&ie=UTF-8&oe=UTF-8&as_umsgid=3208606982556119%40naggum.net&lr=&hl=en

とか

http://kopkillah.com/software/

とか(こっちは本題と関係ない)が紹介されたり.

Message-ID: <86u177hy00.fsf%40cawtech.freeserve.co.uk>

(ecase (intern "a")
(|a| 4)
(|b| 5))
4

Message-ID: <3F68F063.9070704%40dls.net>

(eval (let ((x "a")) `(ecase ,x (,x 4) ("b" 5))))

Message-ID: <87r82f2co5.fsf%40bird.agharta.de>

  (defmacro mycase (key-form &rest clauses)
    (let ((test-key (gensym)))
      `(let ((,test-key ,key-form))
        (cond
          ,@(loop for (keys . forms) in clauses
                  collect (cond ((or (eq keys t)
                                     (eq keys 'otherwise))
                                  `(t ,@forms))
                                ((listp keys)
                                  `((or ,@(mapcar (lambda (key)
                                                    `(equal ,test-key ,key))
                                                  keys))
                                    ,@forms))
                                (t
                                  `((equal ,test-key ,keys)
                                    ,@forms))))))))

  (defun foo (x)
    (mycase x
      (("bar" "baz")
       (print 23) 44)
      ("foo"
       42)
      (otherwise
       'not-found)))

Which yields:

  * (foo "bar")
  23 
  44
  * (foo "foo")
  42
  * (foo "frob")
  NOT-FOUND

case が eql しか使わない場合は実装は最適化されたコードを生成できる.でも equal や equalp を使うと同じようにはいかないよ,とのコメントあり.

Message-ID: <xcvd6dyamjq.fsf%40famine.OCF.Berkeley.EDU>

理論的にはそーだけど,その最適化をやる実装ってある?とのツッコミ.

Message-ID: <4llsmzmqi.fsf%40beta.franz.com>

少なくとも Franz のはやるらしい.詳細はメールで.

Portable ANSI Common Lisp Packages

Message-ID: <ZdJ9b.366305%24cF.109298%40rwcrnsc53>

http://sourceforge.net/projects/com-lisp-utils

syntax-case

Message-ID: <xcvn0d5zy7f.fsf%40conquest.OCF.Berkeley.EDU>

CL で書かれた syntex-case の実装を知りませんか〜,Scheme の syntex-case とい うよりS 式ベースのパターンマッチ言語に興味があるんだけど,という話.

結局 http://www.scheme.com/syntax-case/http://www.cliki.net/fare-matcher が紹介される.どうやら fare-matcher が望むものだったらしく,ハッピーエンド.

Common Lisp メモの fare-matcher のサンプル

Counting occurences of an item in a list: is there a better way?

Message-ID: <bk3kgk%249s%241%40mughi.cs.ubc.ca>

リスト内に出てくる数字 h の出現回数を数えたい,↓のようにしてるんだけど Common Lisp の組み込みでなんかあるんじゃないの?とのこと.

(let ((result 0))
   (loop for i in finals
         do (when (= i h)
              (incf result)))
   result))

Message-ID: <m3ad96bmcy.fsf%40javamonkey.com>

  (count h finals :test #'=)

loop が使いたいなら

 (loop for i in finals counting (= i h))

複数のものを同時に数えたいなら

  (loop for i in finals
    counting (= i h1) into h1s
    counting (= i h2) into h2s
    finally (return (list h1s h2s)))

Message-ID: <m365ju2cb1.fsf%40jcooper02.sagepub.com>

Ob Recursive version:

(labels ((r-count (list count)
           (if list
               (r-count (cdr list) (if (= (car list) test-value) 
                                       (1+ count) 
                                       count))
               count)))
   (r-count <the-list> 0))

Message-ID: <zEn9b.15338%24NM1.11968%40newsread2.news.atl.earthlink.net>

ObRecursionAbstractedOut version だそうです.

  (reduce
    (lambda (count x)
      (if (= x test-value) (1+ count) count))
    <the-list>
    :initial-value 0)

Message-ID: <3f68826f_1%40news.iprimus.com.au>

(reduce + (mapcar
 (lambda (x)
  (if (= x test-value) 1 0))
 <the-list>
 :initial-value 0)

[Or:

(apply + (cons 0 (mapcar
 (lambda (x)
  (if (= x test-value) 1 0))
 <the-list>)))

]

But this is just a filter:

(reduce + (mapcan
 (lambda (x)
  (when (= x test-value) '(1))
 <the-list>)
 :initial-value 0)

Or

(let ((count 0))
 (mapc
  (lambda (x)
   (when (= x test-value)
    (++ count))
 <the-list>)
  count)

Which leads to the other loops suggested earlier in this thread.

fill-pointer/compilation question

Message-ID: <87u17fd01j.fsf%40wh6-307.st.uni-magdeburg.de>

(setq *test* (make-array 0 :element-type 'list :fill-pointer t :adjustable t))
=> #()
(array-has-fill-pointer-p *test*)
=> T

はいいんだけど, (setq user::∗test∗ '#.user::∗test∗) をコンパイルしたファイルをロードしたら

(array-has-fill-pointer-p *test*)
=> NIL

になってしまい,その後の処理でエラーが出て困る,という話.

Message-ID: <87vfrv62jz.fsf%40Astalo.kon.iki.fi>

によれば,どうやらコンパイラは fill pointer を取り除くことが許されているらし い.(CLSH section 3.2.4.2.2 と 3.2.4.4 を参照)

したがって,↓ロード時にベクタを構築すると良いだろう,ということ.

  (setq user::*test*
        (make-array '#.(array-dimensions user::*test*)
                    :initial-contents '#.user::*test*
                    :fill-pointer '#.(fill-pointer user::*test*)))

ただし,このコードでも完全ではないので注意.詳細はメールをどうぞ.

Message-ID: <87smmze0xe.fsf%40wh6-307.st.uni-magdeburg.de>

Thanks for the clarification. 
I had afterwards a hard but fair argument with my
clhs and I think we can still be friends.

This:
 (setq user::*test  
     (make-array
        '#.(fill-pointer user::*test*)
         :adjustable t
         :fill-pointer t
         :initial-contents '#.user::*test*))

Now _seems_ to work.

だそうで….

beginner closures

Message-ID: <3F857177.2F4BC9CA%40eng.cam.ac.uk>

[15]> (setq closurelist nil)
NIL
[16]> (dolist (i '(a b))
              (push #'(lambda () (format t "~a" i)) closurelist))
NIL
[17]> (funcall (first closurelist))
B
NIL
[18]> (funcall (second closurelist))
B
NIL

という動作がわからーんという話.

Message-ID: <m3ad8ampt2.fsf%40javamonkey.com>

マクロ展開で,

(defvar *closures* nil)
(let (i)
  (setq i 'a)
  (push #'(lambda () (print i)) *closures*)
  (setq i 'b)
  (push #'(lambda () (print i)) *closures*)) ;; この時点で i は 'b

という操作になった場合,上記のような動作になる.(CMUCL の dotimes はこんな感じの動作を行なう) どちらも 同じ変数 i を参照しているので

(funcall (first *closures*))
=> b
(funcall (second *closures*))
=> b

という結果になる.これを防ぐためには,それぞれのクロージャに対して明示的に新 しいバインディングを指定してやればよい.つまり↓のような事.

  > (setq *closures* nil)
  NIL
  > (dolist (i '(a b))
      (let ((x i))
        (push #'(lambda () (format t "~a" x)) *closures*)))
  NIL
  > (funcall (first *closures*))
  B
  NIL
  > (funcall (second *closures*))
  A
  NIL

Common Lisp HyperSpec (TM)DOLIST, DOTIMES の解説には

It is implementation-dependent whether dolist establishes a new
binding of var on each iteration or whether it establishes a binding for var
once at the beginning and then assigns it on any subsequent iterations.

とあるので実装依存のようだ.安全確実を期すならば let で明示的にバインディングを作れという事でしょう.