Scheme:初心者の質問箱

Scheme:初心者の質問箱

メーリングリストで質問したり、WiLiKiに自分のページを作ったりするのはちょっと… というシャイなあなたのためのスペースです。

あたらしい質問は、項目の先頭に追加していって下さい。

書き方を間違えても小人さんが直してくれるので、 こわがらなくてもだいじょうぶ。

長くなってきたので過去ログ:



macroexpand の再帰的な評価について

takker? macroexpand を再帰的な評価をすることは可能なのでしょうか。 例えば以下のような、 my-or を macroexpand を行うと最後まで評価されずに終わってしまいます。

(define-macro (my-or . args)
  (if (null? args)
      #f
      (let ((sym (gensym)))
        `(let ((,sym ,(car args)))
           (if ,sym #t
               (my-or ,@(cdr args)))))))

クロージャーなどでは、macroexpand-all などが存在するようなのですが これを自分で定義することは可能なのでしょうか。 いろいろ調べてもよく分からなかったので質問しました。

(define (macroexpand-rec macro form)
  (cond [(null? form) '()]
        [(not (pair? form)) form]
        [(eq? macro (car form)) (macroexpand-rec macro (macroexpand form))]
        [else
         (cons (macroexpand-rec macro (car form))
               (macroexpand-rec macro (cdr form)))]))

(define-macro (%macroexpand-rec macro form)
  `(macroexpand-rec (quote ,macro) (quote ,form)))

;; マクロ展開
(%macroexpand-rec my-or (my-or (eq? 5 5) (eq? 3 4)))

Gauche(0.9.4_pre3)のr7rs環境について

asada? 2014/03/21 07:51:48 UTC

gosh -r7で起動した時,Gaucheの既存のprocedureをimportするには、どうすれば良いですか?regexp?等の正規表現が使いたいのです。

(import (gauche regexp))としたのですが、意図通りにimportできてない様です。 お手数ですが、アドバイスを頂けると助かります。

Window7(Gauche-mingw-0.9.3.3.msi)の gauche-package でエラー

anon(2014/01/05 02:27:58 UTC): 自分のPC上でWiLiKiを使いたいと思って、Apacheをインストールして

#!gosh.exe
(display "Content-type: text/plain\n\n")
(display (gauche-version))
(display "\n")
(display (gauche-architecture))
(display "\nHello World")

というCGIスクリプト(localhost/cgi-bin/test.scm.cgi)が

0.9.3.3
i686-pc-mingw32
Hello World

を返すのを確認した後、DOSプロンプト上でWiLiKiをインストールしようとしたところ、

D:\>gauche-package install WiLiKi-0.6.2.tgz
*** SYSTEM-ERROR: cannot find program '#f': No error
Stack Trace:
_______________________________________
  0  (sys-fork-and-exec (car argv) argv :iomap iomap :directory dir :si ...
        At line 183 of "D:\\Program Files\\Gauche\\share\\gauche-0.9\\0.9.3.3\\lib/gauche/process.scm"
  1  (run-process (cond-expand (gauche.os.windows (win-break-cmdargs cm ...
        At line 51 of "D:\\Program Files\\Gauche\\share\\gauche-0.9\\0.9.3.3\\lib/gauche/package/util.scm"
  2  (clean config dir)
        At line 148 of "D:\\Program Files\\Gauche\\share\\gauche-0.9\\0.9.3.3\\lib/gauche/package/build.scm"
  3  (usage)
        At line 93 of "(input string port)"

となります。 (clean config dir)するためのコマンドが見つからないということなのでしょうか?

ファイルのアクセス日時のみ現在の日時にしたい

2011/10/26 19:49:51 PDT: (touch-file "filename")だと更新日時も変わってしまいます… アクセス日時を変えたいだけなので、実際にファイルを読み込む手間を省く方法はないでしょうか?

refの仕様

2011/07/05 11:41:25 PDT: これ,デフォルトではエラーなんですね.少し引っかかりました.

(ref xs 1.0)

2011/07/05 19:16:14 PDT: Cプログラムを何も考えずにSchemeに移植していたのですが、こう定義すると

gosh> (define-method ref ((l <list>) (r <real>)) (ref l (inexact->exact r))
)
#<generic ref (10)>

これらは動きます

gosh> (ref '(a b) 1.0)
b
gosh> (~ '(a b) 1.0)
b

これはエラーになってしまいます

gosh> (set! (~ '(a b) 1.0) 1)
*** ERROR: no applicable method for #<generic |setter of ref| (6)> with argumen
s ((a b) 1.0 1)
Stack Trace:
_______________________________________

さらにこう定義して期待通りの動作となりましたが、正しいのかわかりません

(define-method (setter ref) ((l <list>) (r <real>) obj)
  (let1 i (inexact->exact r)
    (set! (ref l i) obj)))

syntax-rules の ... の動作

齊藤 (2010/09/29 19:46:49 PDT): syntax-rules のテンプレートについてです。 以下のように書くとエラーになります。

(define-syntax hoge
  (syntax-rules ()
    ((_ r ...)
     (list '(r (r ...)) ...))))

(hoge 1 2 3)

期待する展開形は

(list '(1 (1 2 3)) '(2 (1 2 3)) '(3 (1 2 3)))

です。

... が (r ...) にも掛ってしまうことが原因とは思いますが、 R5RS 的には未定義なんでしょうか? 手元で試してみたところ、 Gauche を含む R5RS 処理系のいくつかではエラーになり、主要な R6RS 処理系では期待通りに展開されるようです。

Shiro(2010/10/02 17:33:18 PDT): はい、これはR5RSでは曖昧で、R6RSで明確化されました。

R5RS section 4.3.2

Pattern variables that occur in subpatterns followed by one or more instances of the identifier ... are allowed only in subtemplates that are followed by as many instances of ....

このas manyは「同数の」ですが、文章全体が必要条件を述べているともとれ、その場合 as manyは下限を示していると解釈可能です。テンプレートの方で...が多い場合については 仕様は何も言っていないので、以下のどちらの解釈もOKです。

R6RSでは...が多い場合について明示的に許され、振る舞いも定義されました。

R6RS section 11.19:

If a pattern variable is followed by more ellipses in the subtemplate than in the associated subpattern, the input form is replicated as necessary.

齊藤 (2010/10/08 21:35:41 PDT) : なるほど。 納得しました。 R5RS 的には使わない方がよさそうですね。


write の出力のカスタマイズ

osn(2010/05/27 22:16:11 PDT): 実数の write での出力では、桁数に応じて固定小数点表記あるいは浮動小数点表記とが切り替わります。

gosh> (write 1.0)
1.0
gosh> (write 10000000.0)
10000000.0
gosh> (write 100000000000.0)
1.0e11

この表記の切り替わりの桁数や浮動小数点表記内容をカスタマイズできないでしょうか。write-object でできないかと思い、以下のようなことをやってみたのですが、効果ありませんでした。

gosh> (define-method write-object ((obj <real>) out) (format out "~a*1000" (/ obj 1000)))
#<generic write-object (3)>
gosh> (write 3e3)
3000.0   (3*1000 と表示されることを期待)

他処理系で生成されたS式のファイルを gauche で読み込んで write とすると数の表記が変わってしまい、できれば元の処理系と同じ規則を定義して出力したいと思っています。 よろしくお願いします。

gauche/gdbm on openSUSE 11.x

osn(2010/04/14 20:15:08 PDT): (便乗したようなタイトルですいません。;) openSUSE 11.1,11.2(gcc-4.4.1) に Gauche-0.8.14,0.9 をインストールしようとしたところ、./configure の結果では、

  optional modules: odbm ndbm gdbm zlib

となっているのに、コンパイル終了時点で、lib/dbm には fsdbm.scm のみしかなく、gdbm インターフェースが生成されないようです。 config.log をみると

configure:11795: checking for dbm_open
configure:11851: gcc -std=gnu99 -o conftest -g -O2     conftest.c -ldl -lcrypt -
lutil -lm  -lpthread >&5
/tmp/ccEbKcoc.o: In function `main':
/root/Gauche-0.9/conftest.c:135: undefined reference to `dbm_open'
...
configure:12871: checking for dbminit in -ldbm
configure:12906: gcc -std=gnu99 -o conftest -g -O2     conftest.c -ldbm  -ldl -l
crypt -lutil -lm  -lpthread >&5
/usr/lib64/gcc/x86_64-suse-linux/4.4/../../../../x86_64-suse-linux/bin/ld: canno
t find -ldbm
collect2: ld returned 1 exit status
...

等が見られるので、dbm インターフェースに関連して、gauche のコンパイルに失敗しているのかも、と想像しているのですが、対策が分からず困ってます。openSUSE 10.x (gcc-4.1.0)等では問題なくインストールできているのですが、、、対応方法をご教示いただけるとありがたくよろしくお願いします。(gdbm は 1.8.3 で、gauche のインストール可否にかかわらず同バージョンです。)

gauche-gdbm on FreeBSD 8.0

WiLiKi のデータベースに gdbm を使っています。 FreeBSD 8.0 で,portupgrade databases/gauche-gdbm に失敗します。 0.9 がBROKENになっているためです。 試しに,Makefile 中の BROKEN をコメントにして make してみると, gdbm.stub がないと怒られます。 0.8.13 のgdbm.stub をコピーしてmakeすると, 実行時エラーになります。

gdbm を使うのはおすすめではないのでしょうか?

対処方法とおすすめのデータベース形式を教えていただけませんか?

suzuki (2010/03/22 14:40 jst) Gauche-0.9 のソースからインストールしました。FreeBSD 8.0-Release です。下記二ヶ所で悩みました。

suzuki (2010/03/22 14:40 jst) FreeBSD の gauche-gdbm-0.9BROKEN状態になっていて、Mark BROKEN with 0.9 updateと記載されています。Makefile は 0.8.13 のものと同じで,ソースディレクトリは,Gauche-0.9/ext/dbm をさしています。
0.9 にまだ対応していないものと思いましたが。。。

文字列上の繰り返しと部分文字列

ziro (2010/03/19 00:53:38 PDT) 勉強のためにいろんな文字列のアルゴリズムを Gauche で書いています.文字列の各文字を順番に見るときには,マルチバイト文字列上でのインデックスによるアクセスは遅いので,string port を使ったほうがいいんですよね.そうして文字列を舐めている途中の二つの状態の間に対応する部分文字列を取得する方法はありますか? 今は port から読んだ文字を別の string port に書いているのですが,どうにも格好がつきません.

Shiro(2010/03/19 04:43:06 PDT): 「遅い」といっても程度の問題で、データのサイズや必要な 性能要件によって変わってきます。インデックスアクセスは入力文字列長に比例するので、 特に問題になるのは「長い文字列に対して頻繁にインデックスアクセスする」という場合です。

従って、(dotimes (n len) (do-something (string-ref str n))) のようなコードは O(N^2)に なってしまいますが、もし取得する部分文字列の総数が定数ならば、

でもいけるかもしれません。

入力文字列が巨大で、substringで深くインデックスするのを避けたいという場合は、 部分文字列の先頭になり得る箇所で、get-remaining-input-stringで半部分文字列 (その箇所以降の文字列)を取得しておく、という手もあります。Gaucheでは 文字列本体は共有されるので、get-remaining-input-stringが返す文字列は 事実上入力文字列の途中を指すポインタみたいなもので、たくさん取得しておいても さほど性能に影響は出ません。さらにマッチングを進めて、切り取るべき文字列の 終端が分かったら、get-remaining-input-stringで取った文字列partial-strに対して (substring partial-str 0 (- end-index start-index)) のようにして切り取ります。 string-takeでもいいです。この場合、文字列のインデックスアクセスのオーバヘッドは 部分文字列の長さに制限され、入力文字列の大きさには影響を受けません。

気になるのがどちらかというと性能よりもコードの簡潔さ、読みやすさだというのなら、 切り取る部分は文字のリストにしといて後でlist->stringするのがたぶんエレガントになると 思います。オーバヘッドはありますがO(N)なのでこれでもstring-refで舐めるよりは ずっと良いと思います。

ziro (2010/03/19 07:26:51 PDT) ありがとうございます.安直には,バイト数ベースの添字で substring ができると簡単だと思いますが,そんなことはできないのでしょう か (incomplete string を使えばできる?).それから,文字列の途中から逆向 きに辿ってみたいと思うのですが,添字を使わないですます方法はありますか?

Shiro(2010/03/19 12:47:08 PDT): これ以上は目的がわからないと何とも言えないのですが、 かなり性能的にシビアな状況なのでしょうか。

substringをincomplete stringに適用すれば(今のところ)バイトインデックスとして 扱われますが、文字インデックスからバイトインデックスを簡単に求める方法が無いので 使いどころが難しいですね。string-portで読み出しつつport-tellでオフセットを記録するって 手はあります (ただし、peek-charするとオフセットがずれるというバグがあるので注意)。

とにかく今、性能が欲しいということでしたら、unofficialなstring-pointerという のがあるにはあります。途中から逆向きということができます。ただし将来無くなるかも しれません。

あとは、アルゴリズム的にインデックスアクセスが綺麗に書ける、ということなら いっそベクタに変換してしまうとか。 綺麗さにこだわるなら読んだ文字を全部リストにしとくとか、 文字列から離れる手もあります。

ziro (2010/03/20 05:24:03 PDT) 私の状況は要件が厳しいというものではなく,毎回再計算が起こるから string-ref は軽い気持ちでは使い難いという程度のものです.紹介して下さった方法の一で書いて実際の文字列に適用してみて,様子を見たいと思います.まともに可変バイト長の文字エンコーディングを扱えるのはとても有り難いことなので,うまく Gauche を使えたらと思います.ありがとうございました.

Shiro(2010/03/20 07:15:28 PDT): それなら、とりあえず

というのが、多分シンプルさと性能のバランスのいいところじゃないかと思います。 それで問題が出てきたら上に挙げた他の方法を試すということで。

More ...