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 |

2008-04-10

CLで学ぶ「プログラミングGauche」 (5.5)

| 09:03 | CLで学ぶ「プログラミングGauche」 (5.5) - わだばLisperになる を含むブックマーク はてなブックマーク - CLで学ぶ「プログラミングGauche」 (5.5) - わだばLisperになる

今回は5.5章から再開です。

一連のエントリを読み返してみると、あたかもCLについて詳しく知っているように書いてしまっている気がしますが、実際のところ全然詳しくないので、間違いがあったら是非ツッコんで頂けると嬉しいです。

5.5 名前と予約語

Schemeには予約語がないそうですが、CLはどうなんでしょう。調べても良く分かりませんでした。

特に予約語という記述もないので、似たような状況だとは思うのですが…。

システムの根幹に関わるものの名前を再定義することは可能だったりはしますが、警告やエラーになったりもします。

また、メジャーな処理系では拡張でパッケージをロックする仕組みもあり、根幹のCOMMON-LISPパッケージ等の内容を上書きで再定義しようとすると、パッケージロックのエラーになります。

5.6 モジュールを使う

GaucheではSchemeの拡張としてモジュールの仕組みがありますが、CLでは標準のパッケージの仕組みが相当するかと思います。

ただ、CLのパッケージは、名前空間を分けるということが主で、モジュールのロードの仕組み等まで規定するものではありません。

Emacsのように、PROVIDEや、REQUIREというものもありますが、Emacsのようにロードする仕組みがきっちりと決まっている訳ではなくて具体的にファイルをロードする仕組みは処理系依存になります。中途半端に悩ましいことも多い気もしますが、最近では、ASDFというモジュールをロードする仕組みがメジャーになりつつあるようで処理系によっては、標準でREQUIREがASDFを呼び出すようになっているものもあります(SBCL等)。

PROVIDEは*modules*にパッケージ名を追加登録するだけのものです。割と、provideでなく(pushnew :foo *modules*)などと書かれていることも多く、また、ASDFでロードされたパッケージも*modules*に追加する人もいれば、いない人もいるという感じでなんだか適当な現状です。

また、CLは#+、#-等で処理系の違いを吸収する記述が可能で、依存個所を分けて記述し、一つのソースを色々な処理系で読み込ませることが普通に行われています。

これは、MacLISPから続く伝統であり、CL、MacLISP、Zetalisp、Franz Lisp等、MacLISP系のLISPであれば、共通のソースファイルを使用することも可能です。(それなりに面倒なこともありますが…。)

5.7〜5.8 SRFI

SRFIはSchemeの共通のライブラリとして非常に有効に機能していますが、CLの人々にはそのような共通のライブラリを持つという考えは薄いのか、割とばらばらな様子です。元から抱えてるものが多いというのもあるでしょう。CLRFIというSRFIを手本にした動きもありますが、活溌ではないようです。

とはいえ、最近では、ASDFが共通の基盤になりつつあるので、定番なものは、ASDFで簡単に導入できるようになっていることが殆どです。

また、ネットワークインストール等に対応したASDF-INSTALLもあり、Debianのapt-getのように、(asdf-install:install :foo)でインストール可能で非常に便利ではあります。

CLのANSI標準が決まってから月日も流れ、今日必要とされている機能が規定されていないことなどが色々と議論されることも多くなってきているようです。

現状CLに足りていないものについては、CL界の大御所であるDaniel Weireb氏が纏めている下記のエントリの後半が非常に参考になります。

ということで、次回は、6章から再開したいと思います。

2008-04-09

CLで学ぶ「プログラミングGauche」 (5)

| 04:33 | CLで学ぶ「プログラミングGauche」 (5) - わだばLisperになる を含むブックマーク はてなブックマーク - CLで学ぶ「プログラミングGauche」 (5) - わだばLisperになる

今回は5章「プログラムの書式」です。

5.1 スクリプト

Gaucheは主にスクリプト言語として開発されていますが、CLの場合、イメージベースなのでスクリプトとして呼ばれるというところは得意としていないと思われます。

全く不可能ではないですが、そこに重きを置いていないようなので手薄になっていて、そういう用途で使う場合には特有の問題に遭遇することが多いです。

この辺の解説は、LispUser.netさんの記事が非常にためになります。

個人的には、UNIXソフトウェアツールの一員としてCLを使うのは、色々面倒臭いので逆にSLIMEからOSを操作しています(LISP側からホストを呼ぶ)。

とはいえ、そういう需要もあるので処理系によっては便利な拡張があります。

CLISPでは、

#!/usr/bin/env clisp

(defun main ()
  (format t "さようなら、世界!!~%")
  (print *args*))

(main)

のようなスクリプトを書いて、実行属性を付けてあげれば、

./hello.lisp "hello" "hello"
さようなら、世界!!

("hello" "hello")

という風に簡単に実行できます。

SBCLにはこの仕組みはありませんが、割と簡単に近いものは作れる気がしたので、作ってみました。

自分は、この方法について良く分かっていないので、もっと良い方法があると思います。

下記の方法は、CL-USERパッケージにファイルをロードして終了するmainという名前の関数を定義し、ssbclというスクリプトからmain関数を呼び出しているだけのものです。

内容はマニュアルのSBCLをシェルスクリプトとして実行するところから拝借しました。

1. 毎回設定をロードさせるのも面倒なので、専用のイメージを作成
;; このファイルをsbcl --load shebang.lispのようにして実行すると、
;; /tmp/shebang.coreが生成される
(in-package :cl-user)

;; #!を読み飛ばす
(set-dispatch-macro-character #\# #\!
			      (lambda (stream char arg)
				(declare (ignore char arg))
				(read-line stream)))

;; デバッガを抑制する
(setf *invoke-debugger-hook* 
      (lambda (condition hook)
	(declare (ignore hook))
	;; Uncomment to get backtraces on errors
	;; (sb-debug:backtrace 20)
	(format *error-output* "Error: ~A~%" condition)
	(quit)))

;; スクリプトが存在するか確認する
(defun probe-script ()
  (and (second *posix-argv*)
       (probe-file (second *posix-argv*))))

;; main / スクリプトを実行して終了する関数
(defun main ()
  (let ((script (probe-script)))
    (when script
      (load script)
      (quit))))

;; イメージを"/tmp/shebang.core"に生成する
;; sbcl --core shebang.coreで起動
(sb-ext:save-lisp-and-die "/tmp/shebang.core" :purify t)
2. 起動用のスクリプトを作成
#!/bin/sh

sbcl --noinform --core shebang.core --eval '(main)' $*
3. 実行したいスクリプト例
#!/home/dir/bin/ssbcl

(defun foo ()
  (format t "さようなら、世界!!~%~A" *package*)
  (print *posix-argv*))

(foo)
4. 試してみる
% ./hello.lisp
>>> さようなら、世界!!
>>> #<PACKAGE "COMMON-LISP-USER">
>>> ("/usr/local/sbcl/1.0.15/bin/sbcl" "./hello.lisp")

5.2 マルチバイト文字の利用

前回のエントリとダブってしまうのですが、CLでも日本語を取り扱うことは可能です。処理系依存だったりして色々面倒なところもあるのですが、SBCL、CLISP、Clozure CL、Allegro CL、LispWorks等主要なところは各種文字コードに対応しているので大抵は大丈夫だと思います。

これまたLispUserさんの記事が非常に参考になります。

5.3 コメント

行コメントも、ブロックコメントもCLと共通です。S式コメントはないので、必要な場合は、リードマクロで作ってみるのも面白いかもしれません。

あまり深く考えて作ってないですが、

(defun S-expression-comments (stream char arg)
  (declare (ignore char arg))
  (read stream t nil t)
  (values))

(set-dispatch-macro-character #\# #\; #'s-expression-comments)

こんな感じで良いのでしょうか。ちなみに、最後に(values)を実行して姿を消すというのは反則かなあとずっと思っていたのですが、CLtL2の22章にも例として載っているのをみつけたので割と定番なのかもしれません。

S式コメントに近いものとしては、LISP古来からの方法だと、

(print "Hello")
=>
'(print "Hello")

という風にクオートしてしまうというのが近いといえば近いかもしれません。

;; 行コメント
(print "Hello")				;行コメント

#| ブロックコメント
  
  #|ネスト可能 |#

|#

;; 自作S式コメント
#;(progn (princ "さようなら、")
       (princ "世界!!")
       (terpri))

(list 1 #;2 3)
=> (1 3)

;; クオートでも似たことは可能、しかし当然ながら返り値は発生する。
'(progn (princ "さようなら、")
       (princ "世界!!")
       (terpri))

5.4 空白

ここも概ね一緒です。

という感じで、長くなったので5.5からは次回にします。

2008-04-08

CLで学ぶ「プログラミングGauche」 (4)

| 03:25 | CLで学ぶ「プログラミングGauche」 (4) - わだばLisperになる を含むブックマーク はてなブックマーク - CLで学ぶ「プログラミングGauche」 (4) - わだばLisperになる

すっかり間があいてしまいました。

今回は4章「Gaucheの開発スタイル」です。

4.1 インタラクティブな実行とREPL

4.1.1 リテラル

このセクションに関しては大体CLも同じですが、複素数の表記方法等ちょっとした違いもあります。

それと、文字のエンコーディングについては実装依存になっているので、処理系ごとに確認する必要がありますが、大体は、external-formatで指定することになるようです。

に詳しく纏められています。

;; 浮動小数点数
3.14

;; 有理数
2/3

;; 複素数
#C(3 4)

;; 真(nil以外のすべて)
t

;; 偽
nil

;; 文字
#\a

;; 日本語 (実装依存)
#\あ
#\HIRAGANA_LETTER_A

;; SBCLでの例
sb-impl::*default-external-format*
=> :utf-8

(code-char 12354)
=> #\HIRAGANA_LETTER_A
(princ (code-char 12354))
>>> あ

;; CLISP、SBCL、Clozure CLでは#\u3042とった表記が可能
(princ #\u3042)
>>> あ

;; 制御文字等
#\Space

#\Newline

;; 文字列
"abc"
4.1.2 手続き呼び出し

このセクションもSchemeのdefineとCLのdefunは意味が違っていること以外は大体共通です。

CLのdefunは、名前に関数の実体を束縛する以外にも色々なことを行っていて、この辺は、LISP-1と、LISP-2とで違いがあります。

;; Scheme: (define sqrt2 (sqrt 2))

(setq sqrt2 (sqrt 2))
;; もしくは
(defparameter sqrt2 (sqrt 2))
(defvar sqrt2 (sqrt 2))
;; 等色々状況によって使い分けが必要

;; 手続きの定義
(defun pythagoras (x y)
  (sqrt (+ (* x x) (* y y))))

(pythagoras 3 4)
=> 5

;; (exit)
;; 大体cl-userで定義されていることが多い。
;; quitだったり、exitだったり処理系によって違う。

4.2 Emacs

ここでLispマシンの話がでてきますが、EmacsのS式操作/開発環境はLispマシンから引き継いだ操作というよりは、最初の1970年代後半のITS EMACSで完成していて、ZMACSは、もちろんEMACSの影響を受けているので、ITS EMACS→ZMACSではなかったかなと思います。ただ同時期なのではっきりしたことは言えません。…非常にどうでも良いことで誰も興味ないと思いますが、敢えて喰いついてみました(笑)

実際のところ、表4-2の快適な移動コマンドの例とC-M-\は30年以上前のオリジナルEMACSに既に全部あるので、もしかしたら、EMACSより前のエディタマクロ集の時点で既に定番だったのかもしれません。また、GNU Emacsのinferior lispモードくらいの開発環境は、PDP-10上でEMACSとMacLISPが連携するLEDITで既に実現していました。

EMACS上のCLの開発環境といえば、最近では、SLIMEが有名ですが、この章で解説されていることは殆どカバーしています。

SLIMEが便利なところとして思い付くところでは、

  • キー操作一つで式ごとコンパイルできる
  • M-.で定義したソースファイルに飛ぶことができる。これは処理系がサポートしていれば、なのですが、Allegro Cl、SBCL、Clozure CLはサポートしています。詳しくは、404 Not Foundに纏められています。ちなみに、この機能は、30年前のMIT CADR Lispマシンに既に存在します。
  • コンパイルしてエラーになったところを色付き下線で表示してくれて、M-p等で該当個所に飛べる(警告とエラーで色が別)
  • シンボル/関数を補完してくれる(補完方式には、色々あってd-bi→destructuring-bindというものや、dbind→destructuring-bind等あり)
  • 関数等のドキュメント文字列を呼び出して綺麗に表示
  • 調べたいシンボルにカーソルを合せてキー操作一つでHyperSpecが引ける
  • 自動で編集しているファイルのパッケージを認識してくれ、パッケージを移動してくれる。
  • カーソル位置のマクロの展開を行なってくれる(マクロ展開表示の上でさらにc-c c-m可能)(再帰的に最終形態まで一度に展開することも可能)
  • 調べたい関数のシンボルにカーソルを合せてキー操作一つでdisassembleの結果を表示してくれる。
  • Emacsや、Xyzzyの*scratch*バッファのように、式を評価すると結果が挿入されるようなモードがある
  • サーバ/クライアント方式なので、同一イメージに複数人が接続して開発することが可能。

等々。

このうちで、自分が気に入っているのは、マクロの展開表示です。

文章で書くと便利そうでもないかもしれませんが、

(do ((i 0 (1+ i)))
    ((= 10 i) 'end)
  (print i))

というマクロがあったとすると、カーソルを式の先頭にもっていってキーを操作すれば、

(BLOCK NIL
  (LET ((I 0))
    (TAGBODY (GO #:G1)
     #:G0    (TAGBODY (PRINT I))
             (PSETQ I (1+ I))
     #:G1    (UNLESS (= 10 I) (GO #:G0)))
    'END))

と別窓で表示してくれるという機能です。さらに、別窓の式も芋蔓式にマクロ展開できます。

この機能は非常に便利だと思うのですが、LISP使いの方と話しても、そんなのREPLで

(macroexpand-1 
 '(do ((i 0 (1+ i)))
     ((= 10 i) 'end)
   (print i)))

ってすれば良いんだよ、とか言われてしまい、便利だと思われないことが多いです。

なんでなんだー!?。まあ、自分の説明が悪いのかもしれません。

という感じで、5章に続きます。

2008-03-18

CLで学ぶ「プログラミングGauche」 (3)

| 23:42 | CLで学ぶ「プログラミングGauche」 (3) - わだばLisperになる を含むブックマーク はてなブックマーク - CLで学ぶ「プログラミングGauche」 (3) - わだばLisperになる

今回は3章「Gaucheの設計思想や誕生の背景」です。

Perlの影響

Gaucheは実用性を重視し、Perlの良いと思われる部分は積極的に吸収したということで、

  • 正規表現リテラル
  • 文字列間補完
  • モジュールシステム
  • DBI/DBD

等がPerlから受けた主な影響だそうです。

この辺は、最近では必須と考えられることが多いと思われるような機能でもあります。

Common Lispではどうかというと、Perl風の正規表現を提供するデファクトと言って良いような存在としては、CL-PPCREパッケージがあり、文字列間補完は、CL-INTERPOLあたりでしょうか。

モジュールシステムについてはちょっと考え方が異なりますが、CLには、標準でパッケージ機能があります。ただし、パッケージ機能は、ソースファイルの配布形態、読み込み方等を規定するものではないので、その辺を補完するものとしては、最近では、ASDFがデファクトになりつつあります。

DBI/DBDの例は、GaucheのPerlからの影響を受けたことを示す例だと思うので、横に置いておいても良いだろうと思われます。もちろんCLにもDB関係のパッケージも色々あります。

Common Lispの影響

勿論ですが、ここの説明は全部Common Lispに含まれています(笑)

  • キーワード引数
  • オブジェクトシステム(CLOS)
  • コンディションシステム

等が主な影響だそうです。

キーワード引数は確かに便利なので、良く使います。それと、ここには挙げられていませんが、オプショナル引数の扱いは、CL方式の方が扱いは単純で、簡単な故に普通に多用されています。

次に、CLOSは非常に強力と言われていますが、強力過ぎとも良く言われるようです。

CLのコンディションシステムもまた、非常に強力で、色々なことが可能です。

数理システムさんのCondition System (PDF)という親切なドキュメントがあるので、詳細はこちらを参照されると良いかと思われます。

ちなみに、私自身は、CLOSもCLのコンディションシステムも全然理解できていません(^^;

しかし、この二つは大規模なアプリケーションや、ミッションクリティカルなものの開発では、必須になってくるものではある気はします。

…まあ、素人が適当に小さいコードを、ちょこちょこ書く範囲ではそれ程深く追及する機会もあまりないということなのかも知れません。

これから出て来るGaucheの例をCLで書いたりして学んでみようと思います。

2008-03-14

CLで学ぶ「プログラミングGauche」 (2)

| 19:12 | CLで学ぶ「プログラミングGauche」 (2) - わだばLisperになる を含むブックマーク はてなブックマーク - CLで学ぶ「プログラミングGauche」 (2) - わだばLisperになる

Gaucheの特徴

今回は2章「Gaucheの特徴」ですが、Gaucheと比較してCommon Lispはどんな感じなのかしらということを書いてみたいと思います。

Gaucheは

  1. 手軽にプログラムを書いて試せるスクリプト言語
  2. 実用規模のプログラムまでスケールする
  3. 他の言語で書かれたアプリケーションに埋め込める

ことに力を入れている、とあります。

それでCommon Lispはどうかというと、UNIX等のOSの上でスクリプトとして手軽に試せる、というのはちょっと苦手です。

頑張ればできないこともありませんが、そこに力は入ってないようです。

ただ、手軽に試せる、ということや、アイディアをさっとスケッチする、ということはSLIMEのような中の環境から逆にOSを操作すれば良い訳で、スクリプト言語でなければ不可能というわけではないと思います。この辺は、Smalltalkに代表されるイメージの中で生活する処理系で共通しているかと思います。換言すれば、現在一般的にスクリプト言語として手軽、というのは、ホストのUNIXと親和性が高いかどうかということで、ホストを含めてイメージとして抱えている言語では、また違ったアプローチになるかと思います。

次に、スケールするかですが、自分は良く知らないのですが数十〜百万行規模のアプリケーションも実稼働しているようなのでスケールはするんじゃないでしょうか。

そして、他のアプリケーションに埋め込み可能かですが、これも最初の項目に関連し、苦手としていると思います。

  • マルチバイトの処理

これはCLでも可能ですが、Gaucheのように環境が整備されていて、かつ、日本のコミュニティが活溌というわけではないので、何か新しいパッケージを試して文字コード特有の問題に遭遇しても、解決するには自力で色々調べたり、直したりする必要に迫られることが多いかと思います。

  • パッケージ、モジュールシステム

Common Lispには標準でパッケージ機能があるので、コードを書く際にも手軽に書き捨てでパッケージを作成して色々試す、ということも広く行われているかと思います。パッケージを作ると名前の衝突等を簡単に回避できるので、この辺もスケーラビリティーも貢献しています。

また、標準ではありませんが、ASDFというパッケージを導入する仕組があり、最近では、ASDFを使って何でも配布することが多くなって来ているようです。

ASDF-INSTALLという、ネットワークインストールに対応した仕組みもあり、DebianのAptのようにモジュールをネットワークインストール可能で非常に便利です。

  • オブジェクトシステム

Gaucheには、CLOSライクなオブジェクトシステムがありますが、CLOSはCommon Lisp Object Systemの略でもありANSI Common Lispで標準になっています。

「実行時メタオブジェクトプロトコルを完全実装したフル動的オブジェクトシステム!」とか、なんだか凄そうなんですが、親切な解説やチュートリアルは(特に日本)あまりなく、残念ながら人口に膾炙している気がしません。

また、CLOS以外にもオブジェクトシステムはあり、AOP等の実装もあります。

  • Cとの連携

C(や他言語)との連携のためのインターフェースもありますが、実装依存で処理系により異なっています。フリーの処理系では、CFFIがメジャーどころかと思われ、殆どCFFIか、UFFIパッケージを使用しているかと思われます。

これらを利用して、OpenGLや、SQLと連携したりするパッケージも多数公開されています。

2008-03-13

CLで学ぶ「プログラミングGauche」 (1)

| 02:32 | CLで学ぶ「プログラミングGauche」 (1) - わだばLisperになる を含むブックマーク はてなブックマーク - CLで学ぶ「プログラミングGauche」 (1) - わだばLisperになる

以前から私は、「プログラミングGauche」が出版された暁にはCommon Lispで内容をさらってみようと思っていました。

この書物はLisp系を実用的に使用するに当って現状で直面するであろう様々な問題に実際に取り組んできたGaucheという言語を使って、実際的なプログラミングを紹介している本というわけで、Common Lispにこの内容が活きない訳はないと思ったからなのですが、実際にぱらぱらと内容を確認してみても、自分のようなLISP初心者が知りたいと思うようなことが程よく纏められているので、順を追って実習して行けば、一通りのことはできるようになるんじゃないかと思いました。

問題は、これが全部CLで実習できるかどうかということですが、Perl互換の正規表現パッケージから継続ベースのウェブサーバまで、一通り似たようなものはあるので、あれこれ代用品を探しつつ実習すれば可能じゃないかと思っています。

  • なぜCommon Lispなのか。

ちなみに、「なぜGaucheではなくてCommon Lispなのか」は自分でも良く分かっていません。

自分は、レトロコンピューティングが好きということもあり、連綿と続くLISPの流れからすると、CLの方が本流に近いから好きというのはあるかもしれません。

それで、自分が考える「連綿と続くLISPの流れ」の印象なんですが、「なんだか適当なんだなあ」です(笑)

また、自分は、リスト操作言語が好きなようなのですが、リスト操作言語として比較すると、CLの方がその性格がちょっと強く出ている気がします。Schemeはリスト操作言語がベースになってはいますが、他に色々目玉があるので、そこに焦点は当てられていないというか、そこを目当てに使ってる人は割と少ないんじゃないかなあと、勝手に想像しています。

リスト操作言語的特徴ってなんなんだ、ということになってしまうかと思いますが、リストでなんでも考えようという所というか、なんというか。

リスト操作で物事を解決するというのも結構美しいと思うのですが、一般的にはどうなのでしょう。

自分は、結構Guy Steel氏の講演Error 404 (Not Found)!!1を見てLISPのこういう面に感銘を覚えました。

また、Arcも原点回帰なのか、元々Paul Graham氏がそういうのが好きなのか分かりませんが、リスト処理的面が強い気がしています。

なんとなく、「S式」、「リスト操作」、「伝統的マクロ」、「適当」の組み合わせが好きな人は、SchemeよりはCLの方が好みなんじゃないかなあ、と想像します。

それはそれで良いとして、とりあえず、1章から

LispとScheme

この章は基礎知識的なところなので、SchemeをCommon Lispに置き換えても別に違いはなく、S式ベースの言語なら大体共通しているかと思います。

という感じで、まったり進めて行きたいと思っています。

Gaucheを知ってる人が読んで、簡単に同じことをCommon Lispでも試して遊べるような、そういうお手軽情報になるという方向も同時に目指しています。