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-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からは次回にします。

GOOでL-99 (P08 連続して現われる要素を圧縮)

| 01:24 | GOOでL-99 (P08 連続して現われる要素を圧縮) - わだばLisperになる を含むブックマーク はてなブックマーク - GOOでL-99 (P08 連続して現われる要素を圧縮) - わだばLisperになる

コレクションクラスに対応してみました。

コレクションクラスがどういう位置付けなのかいまいち分かっていませんが、リストやベクタのスーパークラスのようです。

1stはCLのfirstで、2nd、3rdまで標準で存在しています。

empty?は、コレクションクラスのオブジェクトが空であるかを判定するものです。

(compress "") ;=> ""
(compress '(a a a a b c c a a d e e e e)) ;=> (a b c a d e) リスト
(compress #(a a a a b c c a a d e e e e)) ;=> #(a b c a d e) タプル
(compress #[a a a a b c c a a d e e e e]) ;=> #[a b c a d e] ベクタ
(compress "aaaabccaadeeee") ;=> "abcade" 文字列

(dm compress (u|<col> => <col>)
  (if (empty? u)
      u
      (let ((prev (1st u))
            (but1st (sub u 1 (len u)))
            (res (lst prev)))
        (for ((x but1st))
            (unless (= x prev)
              (pushf res x)
              (set prev x)))
        (as (class-of u) (rev res)))))

QiでL-99 (P24 ロトくじ)

| 00:35 | QiでL-99 (P24 ロトくじ) - わだばLisperになる を含むブックマーク はてなブックマーク - QiでL-99 (P24 ロトくじ) - わだばLisperになる

Qiでも、もう少しでリスト篇が終わり!

(lotto-select 6 49)
\=> [29 5 24 18 23 2]
\

(define lotto-select
  N M -> (rnd-select (range 1 M) N))