Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2010-08-31

KMRCLを眺める(197) FUNCTION-TO-STRING

| 14:27 | KMRCLを眺める(197) FUNCTION-TO-STRING - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(197) FUNCTION-TO-STRING - わだばLisperになる

datetime.lispも前回で眺め終えたので、今回は、KMRCLのequal.lispからFUNCTION-TO-STRINGです。

関数を文字列にするってなんだろうという感じですが、FUNCTION-LAMBDA-EXPRESSIONの結果をどうにか文字列にしている感じです。

(defun function-to-string (obj)
  "Returns the lambda code for a function. Relies on
Allegro implementation-dependent features."
  (multiple-value-bind (lambda closurep name) (function-lambda-expression obj)
    (declare (ignore closurep))
    (if lambda
          (format nil "#'~s" lambda)
      (if name
          (format nil "#'~s" name)
        (progn
          (print obj)
          (break))))))

しかし、FUNCTION-LAMBDA-EXPRESSIONの動作はかなり実装依存なため、大分ばらばらな動きになっています。

(KL::FUNCTION-TO-STRING (LAMBDA (X) X))
; Allegro CL
; ⇒ "#'(LAMBDA (X) X)"

; SBCL
; ⇒ "#'(LAMBDA (X))"

; CCL
(BREAK) ; NIL, NIL, NILが返ってくるため

Allegro CL以外は、有用な結果になってない気がします…。

2010-08-30

KMRCLを眺める(196) DAY-OF-WEEK

| 22:05 | KMRCLを眺める(196) DAY-OF-WEEK - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(196) DAY-OF-WEEK - わだばLisperになる

今回は、KMRCLのdatetime.lispからDAY-OF-WEEKです。

曜日を求めるのには定番のツェラーの公式を利用するものです。

(defconstant* +zellers-adj+ #(0 3 2 5 0 3 5 1 4 6 2 4))

(defun day-of-week (year month day)
  "Day of week calculation using Zeller's Congruence.
Input: The year y, month m (1 ≤ m ≤ 12) and day d (1 ≤ d ≤ 31).
Output: n - the day of the week (Sunday = 0, Saturday = 6)."

  (when (< month 3)
    (decf year))
  (mod
   (+ year (floor year 4) (- (floor year 100)) (floor year 400)
      (aref +zellers-adj+ (1- month)) day)
   7))

動作は、

(STRING (CHAR "日月火水木金土" (KL:DAY-OF-WEEK 2010 8 30)))
;⇒ "月"

というところ

2010-08-29

KMRCLを眺める(195) MONTHNAME

| 23:48 | KMRCLを眺める(195) MONTHNAME - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(195) MONTHNAME - わだばLisperになる

今回は、KMRCLのdatetime.lispからMONTHNAMEです。

定義を読むと、どうも月の数字から西暦の月の名前を割り出す関数のようです。

;; Monthnames taken from net-telent-date to support lml2

(defvar *monthnames*
  '((1 . "January")
    (2 . "February")
    (3 . "March")
    (4 . "April")
    (5 . "May")
    (6 . "June")
    (7 . "July")
    (8 . "August")
    (9 . "September")
    (10 . "October")
    (11 . "November")
    (12 . "December")))

(defun monthname (stream arg colon-p at-p &optional width (mincol 0) (colinc 1) (minpad 0) (padchar #\Space))
  "Print the name of the month (1=January) corresponding to ARG on STREAM.  This is intended for embedding in a FORMAT directive: WIDTH governs the number of characters of text printed, MINCOL, COLINC, MINPAD, PADCHAR work as for ~A"
  (declare (ignore colon-p))
  (let ((monthstring (cdr (assoc arg *monthnames*))))
    (if (not monthstring) (return-from monthname nil))
    (let ((truncate (if width (min width (length monthstring)) nil)))
      (format stream
              (if at-p "~V,V,V,V@A" "~V,V,V,VA")
              mincol colinc minpad padchar
              (subseq monthstring 0 truncate)))))

引数の並びと定義の感じからして、FORMATTERのように使うような気がしましたが、KMRCLで実際に使われている箇所が探し出せませんでした。

(FORMAT NIL #'KL::MONTHNAME 9 NIL NIL)
;⇒ "September"

;; 切り詰めてみる
(FORMAT NIL #'KL::MONTHNAME 9 NIL NIL 3)
;⇒ "Sep"

Googleコード検索で探してみたところでは、

(format nil
        (formatter "~2,'0D-~3/kmrcl::monthname/-~4,'0D ~2,'0D:~2,'0D")
        3 3 2010 10 20)
;⇒ "03-Mar-2010 10:20"

のようなものがみつかりました。

なるほど、こういう風に~//で使ったりもできますね。

2010-08-28

REPLでTwitter

| 01:30 | REPLでTwitter - わだばLisperになる を含むブックマーク はてなブックマーク - REPLでTwitter - わだばLisperになる

Index of /project/cl-twitterというCommon LispのTwitterクライアントがあるのですが、この1ヶ月位試しに使ってみています。

自分の使い方なのですが、SLIMEでREPLにタイムラインを表示させっぱなしにして、つぶやき関数をエディタバッファ上で評価でつぶやきという感じにしています。

コンパイルした結果や警告とつぶやきが混ざって表示されたりしてカオスなのですが、SLIMEを立ち上げっぱなしの人には割と便利じゃないかなあと思っていますが、誰得情報なのかも知れませんw

そしてもちろん、プログラミングの集中の邪魔にはなりますw

とりあえず、自分はこんな感じで必要な範囲で適当に関数を定義して使っています。

;; ユーザーオブジェクト(ログイン)
(DEFPARAMETER *G??????* (TWIT:AUTHENTICATE-USER "G??????" "password"))

;; 牧歌的な複数ユーザー対応
(SETQ *TWITTER-USERS* (LIST *G??????*))

(DEFUN PRINT-ALL-TWEETS ()
  (LET ((ANS () ))
    (DOLIST (USER *TWITTER-USERS*)
      (LET ((TWIT:*TWITTER-USER* USER))
        (SETQ ANS
              (NCONC (TWIT:TWITTER-OP :FRIENDS-TIMELINE)
                     ANS))))
    (TWIT:PRINT-TWEETS 
     (SORT (DELETE-DUPLICATES ANS :KEY #'TWIT::TWEET-ID)
           #'<
           :KEY #'TWIT::TWEET-ID))
    NIL))

;; SLIMEのREPLに表示
(DEFPARAMETER *TWITTER-OUTPUT*
  #.*STANDARD-OUTPUT*)

;; 別スレッドで回す
(DEFUN START-TWIT ()
  (PORTABLE-THREADS:SPAWN-THREAD 
   "twit"
   (LAMBDA ()
     (LET ((*STANDARD-OUTPUT* *TWITTER-OUTPUT*))
       (LOOP (PRINT-ALL-TWEETS)
             (SLEEP (* 6 60)))))))

;; 更新用関数
(DEFUN UPDATE-G?????? (MESG)
  (LET ((TWIT:*TWITTER-USER* *G??????*))
    (TWIT:UPDATE MESG)))

色々便利関数を定義したり、結果をブラウザに表示させてみたり、色々工夫してみるとLISPプログラミングを楽しめるかもしれません。

2010-08-27

KMRCLを眺める(194) UTIME-TO-POSIX-TIME

| 13:04 | KMRCLを眺める(194) UTIME-TO-POSIX-TIME - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(194) UTIME-TO-POSIX-TIME - わだばLisperになる

今回は、KMRCLのdatetime.lispからUTIME-TO-POSIX-TIMEです。

前回のPOSIX-TIME-TO-UTIMEの逆です。

(KL:UTIME-TO-POSIX-TIME (GET-UNIVERSAL-TIME))
;⇒ 1282881649

定義は、単に70年を引いているだけです。

(defun utime-to-posix-time (utime)
  (- utime +posix-epoch+))

2010-08-26

KMRCLを眺める(193) POSIX-TIME-TO-UTIME

| 12:24 | KMRCLを眺める(193) POSIX-TIME-TO-UTIME - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(193) POSIX-TIME-TO-UTIME - わだばLisperになる

今回は、KMRCLのdatetime.lispからPOSIX-TIME-TO-UTIMEです。

Common LispのUniversal Timeは、1900/1/1 0:00からスタートになります。

Posix timeとは70年ずれているわけですが、POSIX-TIME-TO-UTIMEは違いを変換するものです。

(LET ((UT (GET-UNIVERSAL-TIME))
      (PT (PARSE-INTEGER (VALUES (KL:COMMAND-OUTPUT "date +%s")))))
  (LIST :UT UT
        :PT->UT (KL:POSIX-TIME-TO-UTIME PT)))
;⇒ (:UT 3491782344 :PT->UT 3491782344)

定義は、単に70年を足しているだけです。

(defconstant +posix-epoch+
  (encode-universal-time 0 0 0 1 1 1970 0))

(defun posix-time-to-utime (time)
  (+ time +posix-epoch+))

2010-08-24

KMRCLを眺める(192) PRINT-FLOAT-UNITS

| 21:08 | KMRCLを眺める(192) PRINT-FLOAT-UNITS - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(192) PRINT-FLOAT-UNITS - わだばLisperになる

今回は、KMRCLのdatetime.lispからPRINT-FLOAT-UNITSです。

定義は、

(defun print-float-units (val unit)
  (cond
    ((< val 1d-6)
     (format t "~,2,9F nano~A" val unit))
    ((< val 1d-3)
     (format t "~,2,6F micro~A" val unit))
    ((< val 1)
     (format t "~,2,3F milli~A" val unit))
    ((> val 1d9)
     (format t "~,2,-9F giga~A" val unit))
    ((> val 1d6)
     (format t "~,2,-6F mega~A" val unit))
    ((> val 1d3)
     (format t "~,2,-3F kilo~A" val unit))
    (t
     (format t "~,2F ~A" val unit))))

こんな感じですが、なぜdatetime.lispで定義されているのか不思議なところ。

(WITH-OUTPUT-TO-STRING (*STANDARD-OUTPUT*)
  (KL:PRINT-FLOAT-UNITS 0.001234567890 "sec"))
;⇒ "1.23 millisec"

PRINT-FLOAT-UNITSを呼んでいる周りの関数を眺めるとどうもミリ秒とか、そういうのを表記するのに使いたい様子。

2010-08-23

KMRCLを眺める(191) DATE-STRING

| 23:44 | KMRCLを眺める(191) DATE-STRING - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(191) DATE-STRING - わだばLisperになる

今回は、KMRCLのdatetime.lispからDATE-STRINGです。

名前からして日付の文字列を返す感じですが、動作は、

(KL:DATE-STRING)
;⇒ "Mon 23 Aug 2010 23:35:39"

という感じです。

定義は、

(defun date-string (&optional (ut (get-universal-time)))
  (if (typep ut 'integer)
      (multiple-value-bind (sec min hr day mon year dow daylight-p zone)
          (decode-universal-time ut)
        (declare (ignore daylight-p zone))
        (format nil "~[Mon~;Tue~;Wed~;Thu~;Fri~;Sat~;Sun~] ~d ~[Jan~;Feb~;Mar~;Apr~;May~;Jun~;Jul~;Aug~;Sep~;Oct~;Nov~;Dec~] ~d ~2,'0d:~2,'0d:~2,'0d"
                dow
                day
                (1- mon)
                year
                hr min sec))))

となっています。

FORMATが活躍していますが、

(format nil "~[Mon~;Tue~;Wed~;Thu~;Fri~;Sat~;Sun~]" 3)
;⇒ "Thu"

などは使う機会も結構ありそうです。

毎度使いたい時には書式を忘れてますが…

2010-08-22

Smiley Hackathon #9に参加してきました!

| 00:05 | Smiley Hackathon #9に参加してきました! - わだばLisperになる を含むブックマーク はてなブックマーク - Smiley Hackathon #9に参加してきました! - わだばLisperになる

昨日8/21(土)にSmiley Hackathon #9またまた参加させて頂きました!

今回も主催の d:id:acotie さん #9会場提供のOracleさんありがとうございました。

自分は、#4、#5、#6、#7と参加しているので、今回で、5回目。そういえば、前回の#8はShibuya.lisp TT#5と日が被っていて参加できなかったんですねー。

やってたこと

3ヶ月位前のSLIMEのアップデートにより、自分の便利SLIME elispが大分動かなくなっていたので原因を探り修復しました。

殆ど自分にしか役に立ちませんが、折角の機会なのでまとめてgithubにアップしてみました。

交流

arc-users.jp@mgikenさんが参加されていたので、Arcネタで盛り上がりました。

ちなみに、自分はarc-users.jpが2年程前に立ち上がった時のフォーラムの最初のメンバーです!

@sirohukuさんはTwitterやShibuya.lispで面識がありましたが、今回はClojureで色々されていたようです。

@snmstsさんは http://weitz.de/flexi-streams/ の多言語対応回りをハックしていて、今回UTF-8以外の日本語化の目処が立ったようです。drakmaなどのインフラに使われているので進展が楽しみです。

また、@higeponさんも参加されていて、Mosh on MonaでR6RSのテストがすべて通ったとのこと。おめでとうございます!

という風に自分の知ってるLISPerが4人もいて、かなりLISP充なSmiley Hackathon #9でした。

是非また参加したいです!

2010-08-20

KMRCLを眺める(190) PRETTY-DATE-UT

| 22:12 | KMRCLを眺める(190) PRETTY-DATE-UT - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(190) PRETTY-DATE-UT - わだばLisperになる

今回は、KMRCLのdatetime.lispからPRETTY-DATE-UTです。

前回眺めたPRETTY-DATEの結果をDECODE-UNIVERSAL-TIMEして返すものの様子

(defun pretty-date-ut (&optional (tm (get-universal-time)))
  (multiple-value-bind (sec min hr dy mn yr) (decode-universal-time tm)
    (pretty-date yr mn dy hr min sec)))

動作は、

(KL:PRETTY-DATE-UT (GET-UNIVERSAL-TIME))
;⇒ "Friday"
    "August"
    "20"
    "2010"
    "22:11:24"

となっています。

多値での受け渡しは便利なようなそうでもないような。

2010-08-19

Planet Common Lisp 日本 超手抜き版 作ってみました

| 01:01 | Planet Common Lisp 日本 超手抜き版 作ってみました - わだばLisperになる を含むブックマーク はてなブックマーク - Planet Common Lisp 日本 超手抜き版 作ってみました - わだばLisperになる

common-lisp-users.jp をたててみてから早2ヶ月くらい経過するのですが、いまいちやる気もなく放置気味です。

そもそもJPドメインを取る意義としては、提唱者によればSEO狙いらしかったのですが、common lisp users jp でググっても一番上にはでてこない始末w

これでは、JPドメインで運用する意味がないので、何か楽な打開策ないかなあとTwitterでぼやいていたところ@komagataさんより、Planet〜のCL版をやってみたらどうかとの提案がありました。

なるほど!と思い早速作ってみました。

手抜きでGoogle Readerの機能を利用し、URLがやたらと長いので、common-lisp-users.jpで短いURLを付けてリダイレクトしています。

フィードは、自分がウォッチしているLISPerの方々の中でCLの度合いが5割を越えていそうな方をピックアップしました。

勝手にまとめていますのでもし問題があれば登録を外しますのでご連絡下さい。また、リクエストがあれば登録しますので、よろしくお願いします。

どうも状況を打開するというよりPlanet Common Lisp日本版を作ることで満足して終了しまいました。

2010-08-18

KMRCLを眺める(189) PRETTY-DATE

| 21:28 | KMRCLを眺める(189) PRETTY-DATE - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(189) PRETTY-DATE - わだばLisperになる

前回で、impl.lispも眺め終わったので続いて、datetime.lispを眺めます。

ということで今回は、KMRCLのdatetime.lispからPRETTY-DATEです。

定義は、

(defun pretty-date (year month day &optional (hour 12) (m 0) (s 0))
  (multiple-value-bind (sec min hr dy mn yr wkday)
    (decode-universal-time
     (encode-universal-time s m hour day month year))
    (values (elt '("Monday" "Tuesday" "Wednesday" "Thursday"
                   "Friday" "Saturday" "Sunday")
                 wkday)
            (elt '("January" "February" "March" "April" "May" "June"
                   "July" "August" "September" "October" "November"
                   "December")
                 (1- mn))
            (format nil "~A" dy)
            (format nil "~A" yr)
            (format nil "~2,'0D:~2,'0D:~2,'0D" hr min sec))))

となっていますが、与えられた年月日から曜日や月名を多値で返すもののようです。

(KL:PRETTY-DATE 2010 8 18)
;⇒ "Wednesday"
    "August"
    "18"
    "2010"
    "12:00:00"

(APPLY #'KL:PRETTY-DATE
       (CDDDR
        (REVERSE
         (MULTIPLE-VALUE-LIST
          (DECODE-UNIVERSAL-TIME
           (GET-UNIVERSAL-TIME))))))
;⇒ "Wednesday"
    "August"
    "18"
    "2010"
    "21:05:12"

ちょっとした時に便利かもしれません。

2010-08-17

KMRCLを眺める(188) PROBE-DIRECTORY

| 23:21 | KMRCLを眺める(188) PROBE-DIRECTORY - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(188) PROBE-DIRECTORY - わだばLisperになる

今回は、KMRCLのimpl.lispからPROBE-DIRECTORYです。

CLの標準には、PROBE-FILEというのがありますが、これだとディレクトリとファイルで検出結果に区別がつきません。

ということで、処理系依存の機能を使ってディレクトリであるかどうかを調べるものになっています。

定義は、

(defun probe-directory (filename &key (error-if-does-not-exist nil))
  (let* ((path (canonicalize-directory-name filename))
         (probe
          #+allegro (excl:probe-directory path)
          #+clisp (values
                   (ignore-errors
                     (#+lisp=cl ext:probe-directory
                                #-lisp=cl lisp:probe-directory
                                path)))
          #+(or cmu scl) (when (eq :directory
                                   (unix:unix-file-kind (namestring path)))
                           path)
          #+lispworks (when (lw:file-directory-p path)
                        path)
          #+sbcl
          (let ((file-kind-fun
                 (or (find-symbol "NATIVE-FILE-KIND" :sb-impl)
                     (find-symbol "UNIX-FILE-KIND" :sb-unix))))
            (when (eq :directory (funcall file-kind-fun (namestring path)))
              path))
          #-(or allegro clisp cmu lispworks sbcl scl)
          (probe-file path)))
    (if probe
        probe
        (when error-if-does-not-exist
          (error "Directory ~A does not exist." filename)))))

動作は、

(KL:PROBE-DIRECTORY "/etc")
;⇒ #P"/etc/"

(KL:PROBE-DIRECTORY "/etc/hosts")
;⇒ NIL

;; 参考: PROBE-FILE
(PROBE-FILE "/etc")
;⇒ #P"/etc/"

(PROBE-FILE "/etc/hosts")
;⇒ #P"/etc/hosts"

という感じです。

2010-08-16

KMRCLを眺める(187) CANONICALIZE-DIRECTORY-NAME

| 23:09 | KMRCLを眺める(187) CANONICALIZE-DIRECTORY-NAME - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(187) CANONICALIZE-DIRECTORY-NAME - わだばLisperになる

今回は、KMRCLのimpl.lispからCANONICALIZE-DIRECTORY-NAMEです。

与えられたファイル名をディレクトリの名前として正規化して返すもののようです。

:unspecificが返ってきてしまう場合にはNILを返すローカル関数を定義して処理しています。

定義は

(defun canonicalize-directory-name (filename)
  (flet ((un-unspecific (value)
           (if (eq value :unspecific) nil value)))
    (let* ((path (pathname filename))
           (name (un-unspecific (pathname-name path)))
           (type (un-unspecific (pathname-type path)))
           (new-dir
            (cond ((and name type) (list (concatenate 'string name "." type)))
                  (name (list name))
                  (type (list type))
                  (t nil))))
      (if new-dir
          (make-pathname
           :directory (append (un-unspecific (pathname-directory path))
                              new-dir)
                    :name nil :type nil :version nil :defaults path)
          path))))

となっていますが、パス名回りは色々と複雑で処理系とOSによって動作は若干変わってくるようです。

(KL::CANONICALIZE-DIRECTORY-NAME "/tmp/../etc/hosts.allow")
;; SBCL
;⇒ #P"/tmp/../etc/hosts.allow/" 
;; Allegro CL
;⇒ #P"/etc/hosts.allow/"

2010-08-15

ELIS復活祭メモ(4) TAO/ELISのメインエディタZENと開発環境

| 23:30 | ELIS復活祭メモ(4) TAO/ELISのメインエディタZENと開発環境 - わだばLisperになる を含むブックマーク はてなブックマーク - ELIS復活祭メモ(4) TAO/ELISのメインエディタZENと開発環境 - わだばLisperになる

現在Common Lispでメジャーだと思われる開発環境は、Emacs上のSLIMEかなと思います。

このSLIMEなのですが、自分の感覚ではLISPマシンの開発環境であるZmacs(Emacs)+LISP処理系の連携に近いものを感じます。

ということで、TAO/ELISの開発環境はどうだったのかということになるのですが、やはりZen(Emacs)+REPLがメインになるようです。

あまり触ってみる時間がなく、詳しく知ることはできなかったのですが、

  1. TAOのリスナー(REPL)上で、edとかzとするとZenが起動する
  2. Zenからファイルを開き、色々書く
  3. 作成した関数を動かしてみるには、C-x uでカーソルの直前の式を評価、C-x yでバッファのファイルを丸ごと評価(ロード?)
  4. C-x C-cでZenを抜けてリスナーへ
  5. 定義した関数を実行してみる
  6. Ctrl-Eでエディタに戻る

を繰り返すようです。また、M-x shell でZen上からシェル(REPL)が開くこともできます(もちろんTAOが開く)

自分は、使用感的には、CADR系のLispマシンの開発環境よりは、MacLISP+Emacsの環境であるLEDITが近いかなと思いました。

キーバインドもC-x yの挙動やC-eでエディタに復帰するところなどはLEDIT由来なのかなと思われました。MacLISP+EMACSのLEDIT環境はどういうものだったかは、PDP-10のエミュレータ環境で試してみたり、萩谷先生の

で伺い知ることができるかと思います。

リスナーについて特筆すべきところは、一番外側の()は省略できることかと思います。

(fib 10)

と打ち込む代わりに

fib 10

と実行できました。

等々、他にもマクロや、マルチプログラミングの定石な開発手順やデバッグ方法なども知りたかったのですが、やはり時間がありませんでした。

試すことを一覧表にでもまとめて持参するべきだったと後悔〜。

2010-08-14

Common Lispの変数の種類と振舞い

| 23:35 | Common Lispの変数の種類と振舞い - わだばLisperになる を含むブックマーク はてなブックマーク - Common Lispの変数の種類と振舞い - わだばLisperになる

TwitterでCommon Lispの変数の種類と特徴について考える機会があったので箇条書き的にまとめてみることにしました。

が、しかし、あまり良くまとめられませんでした…。(エクステントについても触れていません)

変なところがあったら直しますので教えて下さい。

詳しくは、数理システムさんにまとめのドキュメントがあるので参照されると良いかと思います。

Common Lisp の スコープ と エクステント(PDF)

基本的な事項

CLのトップレベルにレキシカルな束縛はない(ヌルレキシカル環境/空レキシカル環境)
;; トップレベルで
(setq foo 33)

foo
;⇒ 33

;; シンボルのバリューセルに値が格納されている
(symbol-value 'foo)
;⇒ 33
LETではレキシカルな束縛を作れる(LAMBDAでも可)
(LET ((FOO-LEX 33))
  FOO-LEX)
;⇒ 33

(LET ((FOO-LEX 33))
  (SYMBOL-VALUE 'FOO-LEX))
;>>> The variable FOO-LEX is unbound.
LETはスペシャル変数の束縛も作れる
(LET ((FOO-SPECIAL 33))
  (DECLARE (SPECIAL FOO-SPECIAL))
  FOO-SPECIAL)
;⇒ 33
スペシャル変数はシンボルのバリューセルの値が参照される
(LET ((FOO-SPECIAL 33)) ; FOO-SPECIALはレキシカル変数
  (SYMBOL-VALUE 'FOO-SPECIAL))
;>>> The variable FOO-SPECIAL is unbound.

(LET ((FOO-SPECIAL 33))
  (DECLARE (SPECIAL FOO-SPECIAL)) ;スペシャル宣言
  (SYMBOL-VALUE 'FOO-SPECIAL))
;⇒ 33

(PROGV '(FOO-SPECIAL) '(33)
  (SYMBOL-VALUE 'FOO-SPECIAL))
;⇒ 33
ローカルなスペシャル宣言は内側の変数の束縛には影響しない
(LET ((FOO-SPECIAL 33))
  (DECLARE (SPECIAL FOO-SPECIAL)) ;ローカルな宣言
  (LET ((FOO-SPECIAL 42))
    (LIST FOO-SPECIAL (SYMBOL-VALUE 'FOO-SPECIAL))))
;⇒ (42 33)
トップレベルでのスペシャル宣言は浸透的にLETなどの内側にも影響し宣言は取り消せない
(DEFVAR TOP-FOO-SPECIAL 100)

(LET ((TOP-FOO-SPECIAL 33))
  (LET ((TOP-FOO-SPECIAL 42))
    (LIST TOP-FOO-SPECIAL (SYMBOL-VALUE 'TOP-FOO-SPECIAL))))
;⇒ (42 42)
定数は代入も束縛もできない
(DEFCONSTANT FOO-CONSTANT 42)

(SETQ FOO-CONSTANT 33)
;>> FOO-CONSTANT is a constant and thus can't be set.

(LET ((FOO-CONSTANT 33))
  FOO-CONSTANT)
;>> The name of the lambda variable FOO-CONSTANT is already in use to name a constant.

生活の知恵

トップレベルでスペシャル宣言すると同名のシンボルでレキシカル束縛が作れなくなることについて
  1. *foo*というように*耳当て*を付けてスペシャル変数であることを字面で分かりやすくする
  2. トップレベルではsetや、(setf symbol-value)、setqを使いスペシャル宣言を回避(ちなみにsetqはcmuclではスペシャル宣言されるのがデフォルト動作)
トップレベルからレキシカル変数風の動作が欲しい

DEFINE-SYMBOL-MACROを使う。KMRCLのようなライブラリで定義されていることがある

(defmacro deflex (var val &optional (doc nil docp))
  "Defines a top level (global) lexical VAR with initial value VAL,
      which is assigned unconditionally as with DEFPARAMETER. If a DOC
      string is provided, it is attached to both the name |VAR| and the
      name *STORAGE-FOR-DEFLEX-VAR-|VAR|* as a documentation string of
      kind 'VARIABLE. The new VAR will have lexical scope and thus may
      be shadowed by LET bindings without affecting its global value."
  (let* ((s0 (load-time-value (symbol-name '#:*storage-for-deflex-var-)))
         (s1 (symbol-name var))
         (p1 (symbol-package var))
         (s2 (load-time-value (symbol-name '#:*)))
         (backing-var (intern (concatenate 'string s0 s1 s2) p1)))
    `(progn
      (defparameter ,backing-var ,val ,@(when docp `(,doc)))
      ,@(when docp
              `((setf (documentation ',var 'variable) ,doc)))
      (define-symbol-macro ,var ,backing-var))))

動作

(KL:DEFLEX FOO 42)

(DEFUN F ()
  (INCF FOO))

(LET ((FOO 0))
  (DOTIMES (I 10)
    (F))
  FOO)
;⇒ 0
FOO
;⇒ 52

;; しょうがない
(LET ((FOO 33))
  (DECLARE (SPECIAL FOO))
  FOO)
;>> エラー

;; しょうがない
(PROGV '(FOO) '(33)
  FOO)
;⇒ 42

処理系依存の動作

マルチプログラミングについてはCLの仕様では規定されていないのでスレッドの親子関係でのスコープについても処理系依存

処理系依存ではあるものの大体の合意はある様子

スペシャル宣言は、処理系依存の機能で取り消せることもある(例. SBCL)
(PROGN
  (DEFVAR TOP-FOO-SPECIAL 100)

  #0=(LET ((TOP-FOO-SPECIAL 33))
       (LET ((TOP-FOO-SPECIAL 42))
         (PRINT (LIST TOP-FOO-SPECIAL (SYMBOL-VALUE 'TOP-FOO-SPECIAL)))))

  ;; スペシャル宣言取消し
  (SETF (SB-INT:INFO :VARIABLE :KIND 'TOP-FOO-SPECIAL) :UNKNOWN)

  ;; 同じことをしてみる
  #0#
  )
;-> (42 42) 
;-> (42 100)
定数宣言は処理系依存の機能で取り消せることもある(例. SBCL)
(DEFCONSTANT FOO-CONSTANT 42)

(SETF (SB-INT:INFO :VARIABLE :KIND 'FOO-CONSTANT) :UNKNOWN)

(SETQ FOO-CONSTANT 33)
;⇒ 33

(LET ((FOO-CONSTANT 42))
  FOO-CONSTANT)
;⇒ 42

2010-08-13

KMRCLを眺める(186) CWD

| 23:09 | KMRCLを眺める(186) CWD - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(186) CWD - わだばLisperになる

今回は、KMRCLのimpl.lispからCWDです。

CWDはUNIXのcurrent working directoryのことだと思われ、現在位置しているディレクトリを返すもののようですが、現在位置を返すだけではなくディレクトリも指定した場所に移動するようです。

例のごとく処理系依存の切り分けが定義の殆どを占めています。

(defun cwd (&optional dir)
  "Change directory and set default pathname"
  (cond
   ((not (null dir))
    (when (and (typep dir 'logical-pathname)
               (translate-logical-pathname dir))
      (setq dir (translate-logical-pathname dir)))
    (when (stringp dir)
      (setq dir (parse-namestring dir)))
    #+allegro (excl:chdir dir)
    #+clisp (#+lisp=cl ext:cd #-lisp=cl lisp:cd dir)
    #+(or cmu scl) (setf (ext:default-directory) dir)
    #+cormanlisp (ccl:set-current-directory dir)
    #+(and mcl (not openmcl)) (ccl:set-mac-default-directory dir)
    #+openmcl (ccl:cwd dir)
    #+gcl (si:chdir dir)
    #+lispworks (hcl:change-directory dir)
    (setq cl:*default-pathname-defaults* dir))
   (t
    (let ((dir
           #+allegro (excl:current-directory)
           #+clisp (#+lisp=cl ext:default-directory #-lisp=cl lisp:default-directory)
           #+(or cmu scl) (ext:default-directory)
           #+sbcl (sb-unix:posix-getcwd/)
           #+cormanlisp (ccl:get-current-directory)
           #+lispworks (hcl:get-working-directory)
           #+mcl (ccl:mac-default-directory)
           #-(or allegro clisp cmu scl cormanlisp mcl sbcl lispworks) (truename ".")))
      (when (stringp dir)
        (setq dir (parse-namestring dir)))
      dir))))

動作ですが、普通のパスはもとより

(KL:CWD "/tmp")

(DIRECTORY ".")
;⇒ (#P"/tmp/")

論理パスも扱えるようになっています。

(SETF (LOGICAL-PATHNAME-TRANSLATIONS "var")
      '(("tmp;**;*.*.*" "/var/tmp/**/*.*")))

(KL:CWD "var:tmp;")

(DIRECTORY "var:tmp;*")
;⇒ (#P"/usr/local/var/tmp/.fasls/"
     ...)

2010-08-12

ELIS復活祭メモ(3) TAOではマクロがFUNCALL/APPLYできる

| 21:56 | ELIS復活祭メモ(3) TAOではマクロがFUNCALL/APPLYできる - わだばLisperになる を含むブックマーク はてなブックマーク - ELIS復活祭メモ(3) TAOではマクロがFUNCALL/APPLYできる - わだばLisperになる

TAOマクロがFUNCALL/APPLYできるというのをbitのTAOの連載(no title)を読んで知ったので、やはりこれを試さずにはいられません。

ということで、

(mapcar 'and '(1 2 3 4))
;⇒ (1 2 3 4)

(mapcar 'progn '(1 2 3 4))
;⇒ (1 2 3 4)

(apply 'dotimes '((i 8) (print i)))
;->
0 
1 
2 
3 
4 
5 
6 
7

のようなものを実行して喜んでおりました。

mapcarについては、確認するのを忘れてしまったのですが、複数のリストが取れるので

;; 多分
(mapcar 'and
        '(nil t nil t)
        '(1 2 3 4))
;⇒ (nil 2 nil 4)

のようなことができるのではないかと思います。

TAOでは、mapcarにandを渡したいのにできない!というLISP初心者のFAQも問題にならないわけですね。

mapcarにandを渡していたところ天海さんにandはマイクロ(コード)で実装されているということを教えて頂きました。

andのソースを開く際に、M-.のような感じで、マイクロコードのソースに飛んでいたように思います。

このマイクロコードも色々いじれたりするんでしょうか。次にTAOについて伺える機会があれば伺ってみたいです。

2010-08-11

ELIS復活祭メモ(2) ELIS-8200

| 20:56 | ELIS復活祭メモ(2) ELIS-8200 - わだばLisperになる を含むブックマーク はてなブックマーク - ELIS復活祭メモ(2) ELIS-8200 - わだばLisperになる

市販された、ワークステーション型のELISには、ELIS-8100と、ELIS-8200種類あるのですが、復活祭で詳細を聞くまで基本的に同じLISPの処理系ものだと思っていました。

しかし、説明によると、TAOが載っているのは、8100で、8200は8100やTAOの機能を取り込みつつも処理系は、TAOからCommon Lispに移行していたようです。

(!x 10)のようなTAO独自のものは、SETF等に置き換えられたとのこと。

ELIS-8200は、言わばCommon Lispマシンだったのですね。

TAOから、CLに移行した理由は80年代後半当時の時代の要請(ソフトの互換性等)が主なところだったようです。

処理系がCLということで、質疑応答のときに、ELIS Common Lispの*features*にはどういう風な名前で登録されていたのかを伺いました。

質疑応答の時には何分昔の事ということではっきりしないとのことだったのですが、次の日に8200が起動されたときにわざわざ8200の*features*を表示して見せて頂きました!

確認する限りでは、 ELIS でした。やはり、予想通りというところなのですが、実際に確認できて非常に嬉しかったです。

#+ELISのように書く訳ですね。

しかし、書きながら気付きましたが、8100のTAOを調べるのを忘れていました。なんとなくこれもELISが入っていそうですねー。(Symbolicsのように)

2010-08-10

ELIS復活祭メモ(1) (!(member ...))の謎

| 22:31 | ELIS復活祭メモ(1) (!(member ...))の謎 - わだばLisperになる を含むブックマーク はてなブックマーク - ELIS復活祭メモ(1) (!(member ...))の謎 - わだばLisperになる

もっと時系列に並んだ体系的な記録を書きたいところですが、とりあえず断片的にでも書いてゆきます。

ELISの論文を読んでいたときに、

(!item 2)
(!list (list 1 2 3 4))

(!(member item list) (1+ item))
;⇒(1 3 3 4)

のようになるのが非常に不思議だったので、これを実機で試してきました!

結果として、実際にこういう風になりました。

Common Lispでも、CARを挟んでやれば、

(LET ((ITEM 2)
      (LIST (LIST 1 2 3 4)))
  (SETF (CAR (MEMBER ITEM LIST))
        (1+ ITEM))
  LIST)
;⇒(1 3 3 4)

となります。

TAOでも、

(!(car (member item list)) (1+ item))
;⇒(1 3 3 4)

という風に同じ結果になりました。

CLでこういう動作を追加する場合、defsetfなどで追加できます。

(DEFSETF MEMBER (ITEM LIST) (VAL)
  `(LISPWORKS:WITH-UNIQUE-NAMES (ITEM LIST VAL)
     (SETF (CAR (MEMBER ,ITEM ,LIST))
           ,VAL))))

これで、

(LET ((ITEM 2)
      (LIST (LIST 1 2 3 4)))
  (SETF (MEMBER ITEM LIST)
        (1+ ITEM))
  LIST)

とも書けます。

恐らくですが、TAO/ELISでも、こういった定義があるのではないかなあと想像しています。

2010-08-09

ELIS復活祭参加してきました!

| 23:53 | ELIS復活祭参加してきました! - わだばLisperになる を含むブックマーク はてなブックマーク - ELIS復活祭参加してきました! - わだばLisperになる

8/7〜9でELIS復活祭参加してきました!

運営事務局の皆様、発表者の皆様、素晴しいイベントをありがとうございました!!

割と斜め上な視点ですが、このブログに色々まとめてみたいと思っています。

2010-08-08

KMRCLを眺める(185) COPY-FILE

| 21:42 | KMRCLを眺める(185) COPY-FILE - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(185) COPY-FILE - わだばLisperになる

今回は、KMRCLのimpl.lispからCOPY-FILEです。

名前からしてファイルをコピーするもののようです。

定義は、

(defun copy-file (from to &key link overwrite preserve-symbolic-links
                  (preserve-time t) remove-destination force verbose)
  #+allegro (sys:copy-file from to :link link :overwrite overwrite
                           :preserve-symbolic-links preserve-symbolic-links
                           :preserve-time preserve-time
                           :remove-destination remove-destination
                           :force force :verbose verbose)
  #-allegro
  (declare (ignore verbose preserve-symbolic-links overwrite))
  (cond
    ((and (typep from 'stream) (typep to 'stream))
     (copy-binary-stream from to))
    ((not (probe-file from))
     (error "File ~A does not exist." from))
    ((eq link :hard)
     (run-shell-command "ln -f ~A ~A" (namestring from) (namestring to)))
    (link
     (multiple-value-bind (stdout stderr status)
         (command-output "ln -f ~A ~A" (namestring from) (namestring to))
       (declare (ignore stdout stderr))
       ;; try symbolic if command failed
       (unless (zerop status)
         (run-shell-command "ln -sf ~A ~A" (namestring from) (namestring to)))))
    (t
     (when (and (or force remove-destination) (probe-file to))
       (delete-file to))
     (let* ((options (if preserve-time
                         "-p"
                         ""))
            (cmd (format nil "cp ~A ~A ~A" options (namestring from) (namestring to))))
       (run-shell-command cmd)))))

となっています。

Allegro CLだと同名の関数があるようですが、それを模した感じになっていて、基本的には、CLからUnixのコマンドを実行しています。

動作は、

(KL:COPY-FILE "/etc/fstab" "/tmp/")
;⇒ 0

$ ls /tmp
... fstab ...

となっています。

2010-08-06

(46)TAO/ELIS上でのCプログラミング環境(1986)

| 22:54 | (46)TAO/ELIS上でのCプログラミング環境(1986) - わだばLisperになる を含むブックマーク はてなブックマーク - (46)TAO/ELIS上でのCプログラミング環境(1986) - わだばLisperになる

ELIS復活祭もいよいよ明日!。方向音痴の自分がJAISTまで無事辿り着けるかが心配です!

TAO関係の論文を漁っておりますが、今回は、

です。

前回は、ELISの上にCommon Lispをフルセットで実装したという内容でしたが、今回は、LISPではなくCのプログラミング環境を載せたという内容です。

Lispマシンというと、Lispしか走らないイメージがありますが、Symbolics等でも、Zeta-CというCの処理系や、Ada、Fortran、Pascal、Prolog等の処理系が動いたようです。

本文で説明されている、TAO/ELIS上のCでは、CからTAOに翻訳する方針を採用したそうです。

これにより、LISPのような対話的でインクリメンタルな開発環境が実現できたとのことで、どうしてもTAOに翻訳できないところや、スピードが必要なところでは、マイクロコードで実装したとのこと。

これらのお蔭でCとTAOの関数や変数は等価なものとして扱うことができるそうで、相互に呼び合うことができるとのこと。

また、性能も良好だった様子。色々と凄いですねー。

2010-08-05

(45)TAO/ELIS上でのCommon Lispの実現(1986)

| 22:54 | (45)TAO/ELIS上でのCommon Lispの実現(1986) - わだばLisperになる を含むブックマーク はてなブックマーク - (45)TAO/ELIS上でのCommon Lispの実現(1986) - わだばLisperになる

TAO関係の論文を漁っておりますが、今回は、

です。

気付けば、ELIS復活祭も明後日。2日目の復活イベントでは参加者もELISを触ってみることができるような気配があるのですが、楽しみです!

さて、本文の内容ですが、ELISの上にCommon Lispをフルセットで実装したという内容です。

前回眺めた「Common Lispについて(1985)」の内容を実際に実装してみせたという感じであわせて読むと面白いと思いました。

しかし、批判だけでなく実際に実装してみせるいうのは、凄いパワーですねー。

2010-08-04

KMRCLを眺める(184) COMMAND-LINE-ARGUMENTS

| 23:29 | KMRCLを眺める(184) COMMAND-LINE-ARGUMENTS - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(184) COMMAND-LINE-ARGUMENTS - わだばLisperになる

今回は、KMRCLのimpl.lispからCOMMAND-LINE-ARGUMENTSです。

COMMAND-LINE-ARGUMENTSは、起動されている処理系自身の引数を調べるためのもので、大抵の処理系には付いてることが多いみたいです。

定義は、

(defun command-line-arguments ()
  #+allegro (system:command-line-arguments)
  #+sbcl sb-ext:*posix-argv*
  )

のようになっています。

Allegro CLとSBCLの場合しか書かれていませんが、(apropos 'argv)などとすると関数や変数が見付けられる処理系も多いようです。

SWANK別起動でSLIMEを使っている自分だと、

(KL:COMMAND-LINE-ARGUMENTS)
;⇒ ("/var/lisp/swank-sbcl")

のような感じになっています。

2010-08-03

KMRCLを眺める(183) QUIT

| 12:47 | KMRCLを眺める(183) QUIT - わだばLisperになる を含むブックマーク はてなブックマーク - KMRCLを眺める(183) QUIT - わだばLisperになる

KMRCLos.lispも眺め終えたので今回からimpl.lispを眺めます。

今回は、QUITです。

QUITは良く使うけど、実は処理系依存というものの代表格かなと思います。

定義は、

(defun quit (&optional (code 0))
  "Function to exit the Lisp implementation. Copied from CLOCC's QUIT function."
    #+allegro (excl:exit code :quiet t)
    #+clisp (#+lisp=cl ext:quit #-lisp=cl lisp:quit code)
    #+(or cmu scl) (ext:quit code)
    #+cormanlisp (win32:exitprocess code)
    #+gcl (lisp:bye code)
    #+lispworks (lw:quit :status code)
    #+lucid (lcl:quit code)
    #+sbcl (sb-ext:quit :unix-status (typecase code (number code) (null 0) (t 1)))
    #+mcl (ccl:quit code)
    #-(or allegro clisp cmu scl cormanlisp gcl lispworks lucid sbcl mcl)
    (error 'not-implemented :proc (list 'quit code)))

のようになっています。

処理系ごとに属するパッケージも違うことが良く分かります。

QUITが標準でない理由は自分も良く分からないのですが、LISPマシンのような場合を考えると処理系を終わらせてOSに抜けるとも限らないからなのかなと思ったりしています。

2010-08-01

(44)Common Lispについて(1985)

| 23:35 | (44)Common Lispについて(1985) - わだばLisperになる を含むブックマーク はてなブックマーク - (44)Common Lispについて(1985) - わだばLisperになる

TAO関係の論文を漁っておりますが、今回は、

です。

題名からするとTAOとはあまり関係のない感じですが、内容はTAO開発者からみたCommon Lispという感じです。

大きく分けると、

  1. レキシカル・スコーピング採用について
  2. setf
  3. Keyword Parameter
  4. Package
  5. Sequence
  6. 多重プログラミング
  7. オブジェクト指向プログラミング
  8. インタプリタとコンパイラ

について言及されています

レキシカル・スコーピング採用について

ダイナミック・スコーピングを減らすという流れには賛成だけれど、レキシカル・スコーピングまでは過剰仕様ではないかという指摘。

TAOでは、Static Scopingというものを採用していましたが、静的範囲を越えるところでは変数の探索モードを変えて外側の変数は、スペシャルでない限りアクセスできないようにしているとのこと。

どういう動作になるのか詳しくは分かりませんが、

(let ((x 8))
  (de bar ()
    x))

(let ((x 8))
  (bar))
;>>> エラー

こういうのはエラーで、

(let ((x 8))
  (declare (special x))
  (de bar ()
    x))

(let ((x 8))
  (declare (special x))
  (bar))
;⇒ 8

これならOKということでしょうか。

レキシカル・スコープの問題としては、レキシカルクロージャーを生成する必要があるのでインタプリタでの動作が遅くなる、ということが挙げられています。

これだと、最近のScheme的なクロージャー万歳なプログラミングスタイルから見ると困るんじゃないかと思ってしまいますが、MIT系のLispマシンや、TAOには、closureという構文があり、

(defun f (x)
  (closure '(x)
    (lambda () (incf x))))

(!foo (f 8))
(funcall f)
;⇒ 9
(funcall f)
;⇒ 10
...

のように明示的に取り込む変数を指定すればクロージャーは作れました。

また、「special変数が閉包に入らないのは実際の使用局面で不便を感ずることが多いのではなかろうか」というのは、closure構文では、スペシャル変数も取り込めるので、

(setf (symbol-function 'print-in-base-16)
      (let ((*print-base* 16.))
        (closure '(*print-base*) 'print)))

(print-in-base-16 10))
;-> A
;=> 10

のような局面を指しているのだと思われます。(これはCommon Lispの仕様では無理です)

setf

TAOには、SETFより汎用的な代入機構があるので、これの解説です。

Keyword Parameter

インタプリタでは動作が遅くなるのと、それまでの慣用述語である、

(memq 'x '(x y z))

(member 'x '(x y z))

などが、

(member 'x '(x y z) :test #'eq)

(member 'x '(x y z) :test #'equal)

に統一されたのが人工的だなあ、とのこと。

Package

Symbolのパッケージへの帰属をpresentとownedに分けているのが複雑ではないかという指摘です

Sequence

オブジェクト指向の枠組みがあるのであればポリモーフィズムに吸収した方がスマートではないか、とのこと。

これは、現在でもCLで良く言われていることですね。(ANSI CLでCLOSが統合された時にも全面的な統一はされませんでした)

多重プログラミング

CLの仕様では何の規定もされていないが、実際に実現するのには、色々問題がありそうだ、という指摘です。

これも、ANSI CLでも規定されなかったので、現在でも問題になっていますね。

オブジェクト指向プログラミング

当時のCLtL1では、オブジェクト指向プログラミングは盛り込まれていませんでしたが、取り込む際の問題点が指摘されています。

インタプリタとコンパイラ

当時、Common Lispはコンパイルして使う言語との認識が広まっていて、当時の実装もインタプリタが極端に遅いことを指摘し、インタプリタとコンパイラの両立を掲げていたのに、実際のところインタプリタは付け足し機能のように使われていて、実質的には両立していないではないかとの指摘。

インタプリタの性能と使い勝手にこだわるTAOの姿勢が伺えます。

最近のCL実装は、SBCLにしろ、CCLにしろコンパイラ指向のものが多いのですが、デバッグ時のステップ実行等の使い勝手はあまり良くありません。

インタプリタだとどういう使い勝手だったのか自分も知りたいところです。

まとめ

とはいえ、CLを否定しているわけではなく良いところはTAOに取り込む方向で進んでいて、合せられるところは、極力CLに合せるとのことで、当時のLispマシンの処理系がそうであったように、CLのスーパーセットとしても機能するように考えていたように思われました。