Scheme:初心者の質問箱:log01

Scheme:初心者の質問箱:log01


carを循環させたリストをdisplayするとSegmentation fault

teppey2009/08/08 09:51:38 PDT 以下のプログラムを実行すると、"("をたくさん出力した後にSegmentation faultします。writeでも同様でした。これは、そういうものでしょうか。(0.8.14)

(define x (list 1))
(set-car! x x)
(display x)

variable capture in gauche.test ?

naotoakiyama2009/07/14 18:47:55 PDT: test* に test という変数名を渡すとエラーになります。 (0.8.14)

(use gauche.test)
(define test 1)
(test* "test" 1 test)
;; => *** ERROR: invalid application: (1 "test" 1 #<closure #f>)
;; A convenient macro version
(define-macro (test* msg expect form . compare)
  `(test ,msg ,expect (lambda () ,form) ,@compare))

test を (with-module gauche.test test) にしたら問題ないようでした。 使い方の問題か、バグか分かりませんが、とりあえず。

WiLiKiがqdbmで動作しない

cudo(2009/03/25 23:35:13 PDT)WiLiKiがgdbmでは動作しますが、qdbmでは動作しません。

単に新規サイトを作るだけならgdbmでもいいのですが、既存のWiLiKiサイトの移行を考えておりそのサイトがqdbmで動作しているため、qdbmで動作させたいと考えています。

Gauche、qdbm、WiLiKiのバージョンは以下の通りです。

/usr/share/gauche/site/lib/dbm/qdbm.scm は存在するにも関わらず、下記の通りApache(2.2.3)のエラーログでは「qdbm.scmがない」と言われています。

gosh: "error":
Compile Error: cannot find file "dbm/qdbm.scm" 
in *load-path* ("/usr/share/gauche/site/lib" "/usr/share/gauche/0.8.14/lib")
"/var/www/cgi-bin/wiliki.cgi":4:(use dbm.qdbm)
Premature end of script headers: wiliki.cgi

また、goshを対話モードで実行し、qdbmデータベースにアクセスすると問題なくアクセスできました。

gosh> (use dbm.qdbm)
#<undef>
gosh> (define *db* (dbm-open <qdbm> :path "wikidata.dbm" :rw-mode :read))
*db*
gosh> (define val (dbm-get *db* "WiLiKi"))
(ページの内容)
#<undef>
gosh> 

以上、何か解決方法をお教えいただけたら幸いです。よろしくお願いします。

変数名を値とする文字列から変数の値を得る方法

インスタンスを作成するマクロを書く場合のクォートの仕方について

あらかじめ定義したクラスfooがあるとして、

(create foo ...なんかいろいろ)

と記述することで、fooのインスタンス#<<foo>>を生成するマクロを定義したいとします。

これは、(create <foo> ...)みたいな呼びかたをする場合には以下のように定義できます。(argsの処理は略)

(define-macro (create klass . args)
  '((with-module gauche) make ,klass))

そうではなく、(create foo ...)というように書きたいので、

(define-macro (create klass . args)
'((with-module gauche) make <,|klass|>))

などとすればよいのかとも思いましたが、エラーとなるため違うようです。 こういった場合にどのように記述すればいいのでしょうか。

Shiro(2008/11/12 03:15:26 PST): コード例がおかしいですね。

(define-macro (create klass . args)
  `((with-module gauche make) <,|klass|>))

と書きたかったのだと想定して進めます。(2行めの頭はバッククオート、およびwith-moduleは 引数を2つとる)。

で、2行目は実は次のように書いても全く同じことになります。

   `((with-module gauche make) < (unquote klass) >))

つまり、'<' や '>' は独立したシンボルとして読まれます。くっつけて書いたからといって <foo>のようなシンボルとしてくっついてくれるわけではありません。

fooから<foo>のようなシンボルを作りたければ、文字列を組み立ててstring->symbolで シンボルにするところまで書いてやる必要があります。

(define-macro (create klass . args)
  (let ((class-name (string->symbol #`"<,|klass|>")))
    `((with-module gauche make) ,class-name))))

モジュール・マクロ・ eval についての質問

Shiro(2008/11/10 23:14:03 PST): マクロ呼び出し時のモジュールも 引数で取るようにfooを 定義すればいいんですが、この例だとわざわざevalを使う必要性がわからないので それが根本的な解決になってるかどうかわからないです。

そもそも、マクロ展開はコンパイルタイムに行われるもの、 evalはランタイムの束縛を参照するものなので、混ぜるのは難しいですよ。 上の短い例でも、proc-in-userの束縛は実行時に有効になりますが、 fooの展開はコンパイル時に行われるため、evalもその時に呼ばれます。 すなわち、たとえevalがマクロ呼び出し時のモジュールを受け取れたとしても、 evalの時点でproc-in-userの束縛がまだ存在していない可能性があります。 (Gaucheは通常、トップレベルフォームごとにコンパイルと実行をインターリーブするので スクリプトとして上の例を実行したらproc-in-userの束縛はfooのコンパイル時には 存在していますが、gencompでプリコンパイルする場合はそうなりません)

evalが必要な理由によって、解は異なってくると思います。

Theoria?(2008/11/11 00:01:41 PST) eval がどうしても必要なのは、 DSL で (:bit-vector <list> (lambda ...)) のような書き方をしたいからです。当該 DSL はキーワード・クラス・手続きをサポートしているわけですが、それらをマクロ展開時に判別し、適切にコードを構成してゆくために eval してみる必要があるわけです。確かに kv-list や alist のような書き方をすれば、必要なくなりますが、大変不格好です。

などと書いていたら、解決の可能性を発見しました。つまり「手続きである」と評価された仕様が、更なる手続きではなく、リストを返すようにすればよいのです。そうすれば、評価タイミングを一つずらせると。

Shiro(2008/11/11 01:09:52 PST): もう解決したそうなのでいいんでしょうが、 「普通」の戦略としてはDSLの展開ルーチンはコンパイラと同じドメインに 属します。コンパイラが<list>を読んだ時にそれは単なるシンボルでしかない、 という性質をDSLも持つわけです。<list>の実行時の値で動作を変えたいなら、 「実行時に<list>の値を調べてディスパッチする」というコードへと展開 するしかありません。(端的な例として、DSL展開時に無理やり<list>の値を 調べてそれに特化したコードを出したとして、実行時に(set! <list> :foo) されたら どうします?)

むろん定数値へのimmutableな束縛についてはそれが意味的に実行時 束縛であってもコンパイラが知ることができ、そこに最適化の余地が生まれるわけですが (Gaucheで組み込み手続きがインライン展開されることなどはその例)、それは コンパイラが実行時束縛を「エミュレート」しているからです。DSLで似たようなことを やりたければDSL展開器が同様に実行時束縛を管理しないとならないでしょう (「何をimmutableな束縛とみなすか」はDSLのデザインによって異なるので、 DSLが管理するしかない)。


例外と継続

プログラムの実行中、例外が発生してもプログラムを停止 させたくない場合、継続を使えばいいのでしょうか?

(let/cc cont
  (guard (e (else (cont #f)))
    (error "message")))

リストの構築

入力としてフラットなリストを受け取って、そのリストの#\(と#\)で囲まれた要素をリストでくるむ関数を作成したいのですが、どのようにプログラムを書いたらよいですか?

#\(と#\)がバランスしていないときはシンボルerrorを返します。

実行結果:

gosh> (mk '(1 2 #\( 3 #\) 4))
(1 2 (3) 4)
gosh> (mk '(1 #\( #\)))
(1 ())
gosh> (mk '(1 2 #\( 3 #\( 4 5 #\) 6 #\)))
(1 2 (3 (4 5) 6)))
gosh> (mk '(1 2 #\())
error
gosh> (mk '(1 #\( #\) #\)))
error

Shiro(2008/09/26 17:04:59 PDT): いくつも書き方はありますが、重要なのは 「作るものはプッシュダウンオートマトンなので、 (<現在の入力状態>, <スタック>, <これまでの結果>) の3つの状態をどうにかして 持ち回る必要がある」というポイントを押さえることです。

素直にこれらの状態を引数にしてループで回したものがこれ。 たぶん今回の入力の形式では一番読みやすい形かと。

(use util.match)
(define (mk lis)
  (define (loop xs stack r)
    (match xs
      [()         (if (null? stack) (reverse r) 'error)]
      [(#\( . xs) (loop xs (cons r stack) '())]
      [(#\) . xs) (if (null? stack)
                    'error
                    (loop xs (cdr stack) (cons (reverse r) (car stack))))]
      [(x . xs)   (loop xs stack (cons x r))]))
  (loop lis '() '()))

内部defineでloopを定義しましたが、named letにしても構いません。

カッコがネストするたびに(非末尾)再帰呼び出しをすることで、スタックを 明示的に持ち回らずに処理する手もあります。ただし、再帰呼び出しから (<現在の入力状態>, <これまでの結果>)という二つの状態を戻してやらなくては ならず、多値を使ったりして面倒になります。入力を副作用で変化させるようにすると 結果だけを戻せば良いので多少単純になります。

(define (mk lis)
  (let/cc break
    (let rec ((r '())
              (nested? #f))
      (if (null? lis)
        (if nested? (break 'error) (reverse r))
        (case (pop! lis)
          [(#\() (rec (cons (rec '() #t) r) nested?)]
          [(#\)) (if nested? (reverse r) (break 'error))]
          [else  => (lambda (x) (rec (cons x r) nested?))])))))

(pop! lis) のところで入力を副作用で取っています。この二つの例だけ見ると 最初の方が読みやすいですが、パージングを行うコードは入力をポートから 取ることが多く、その場合はポートからの読み込みがそもそも副作用ですから、 下の方式の方が読みやすくなることもあります。特に、カッコの種類が 複数あって扱いが違ったりする場合(リスト、配列、ハッシュとか)、 それぞれのカッコを扱う内部関数を定義して相互再帰すると綺麗に書けます。

また、今回はエラー時にシンボルerrorを戻り値とするという仕様なので、下の方式のように 呼び出しがネストしているとerrorを戻すために少々工夫が必要です (例ではlet/ccを 使いました)。単にエラーを投げれば良いというだけならerror関数が使えるので let/ccの部分は不要になります。

スタックを非末尾再帰で代替し、しかし多値も副作用も嫌うなら、継続渡し形式で 実装する方法もあります (継続手続きが「残りの入力」と「それまでの結果」を受け取る)。 今回の場合はむしろ冗長になってしまうでしょうが、練習のために書いてみるのも よいかもしれません。

他に、スタックを副作用でpush!/pop!して、入力と結果を引数で回す、というような 手もあります。Schemeではあまりやりませんが、手続き型言語なら そういう実装の方が書きやすいかもしれません。

もしこれまでにプッシュダウンオートマトンの話を聞いたことがなかったら、 ぜひオートマトンの基本を勉強してみてください。何も知らないでも 試行錯誤していればいずれ解けると思いますが、「何らかの形でスタックを持つ必要がある」と 知っているのといないのとでは見通しが全然違うと思います。

define と set!

osn(2008/09/24 23:36:38 PDT): (define a ..) では、記憶域に .. が確保されて、そこのアドレスが a に関連づけられ、(set! a ....) では、a が差すアドレスはそのままで、記憶域の内容が .... に変更される、という理解でいいんでしょうか。

Shiro(2008/09/25 03:33:25 PDT): 「アドレス」というのを考えなくていいと思います。

もちろん、aという名前のついた箱があって、そこに最初は<expr>の結果を指すような 矢印につながってて、(set! a <expr2>) したら矢印が<expr2>の結果を指すように切り替わる、 というイメージでも良いです。ただし、C言語のように「aのアドレス」をとる方法はありません。

(define a (list 1 2 3)) (set-car! a 3) とすると、aがさしているペア、 つまり当初はcarが1でcdrが(2 3)であるようなペアのcar部が3に書き換わるので aの値は(3 2 3)になります。変数aが指している実体 (ペア) そのものは変わっていません。 (ところで、どうして「許されない」と思ったのでしょう? (define a '(1 2 3)) (set-car! a 3) は許されないんですが、それはリテラルリスト (C言語風に言えばconst宣言されてるデータ) を変えようとするからです。)

osn(2008/09/25 05:56:19 PDT): Shiro さん、コメントありがとうございました。

というふうに完全に思い違いしてました。

リテラルリストの変更の件も了解しました。これは、'(1 2) を (list 1 2) に短絡してました。(Gauche:ImmutableObject も参考になりました。) もう1点だけ。今回の質問、そもそもは、(define a '(1)) (define b '(1)) (eq? a b) -> #f なのに (define a 1) (define b 1) (eq? a b) -> #t となって、前者はいいとして、後者はなぜ? と悩み出したのが遠い発端なんですが、これはどう理解したらいいでしょうか。

object-apply を以前の値に戻す方法

osn(2008/09/22 05:43:33 PDT):(define-method object-apply ... )を行って、その後、元の定義に戻すにはどうしたらいいでしょうか。define-method 実行前の状態を取り出して保存しておいて後でその状態で再定義する、あるいは、デフォルトの定義に戻すということができれば、と思っています。

parse-options (gauche.parseopt) についての質問

osn(2008/09/08 08:09:05 PDT):ハイフンで始まらない引数に遭遇した時点でパースを中止、となってますが、else節で受けられるようにはできないでしょうか。
コマンドライン引数としては、ハイフンで始まるオプションに加え、例えば単にファイル名の羅列を渡したりすることがあると思いますが、現在の仕様ではそのように混合した状態が扱いにくく感じています。(機能の理解が不十分でしたらすいません。)

(parse-options args
  (...
   (else (option args continue)
      ...)))

としたときに、option に、例えば "" or #f を渡すようにしてもらえると、コマンドライン引数を全部 parse-options の中で処理できるようになるかなぁ、と。そう変更して、嬉しい場合はあっても、デメリットはあまりないのでは、と期待してます。

外部プロセス呼び出しができない

初めて書き込みさせていただきますcudoと申します。

以下のような手続きでgaucheから外部プロセスを呼び出し、 その結果を得たいと考えていますが、ある環境ではうまく行きません。 詳細は以下の通りです。

【手続き】

(use gauche.process)
(run-process '(ls -l))

【環境と実行の可否】

環境1:MacOSX(10.4.11,Darwin 8.11.1),gauche 0.8.13 (utf-8,pthreads)(ローカルで実行) →問題なし

gosh > '''(use gauche.process)'''
#<undef>
gosh > '''(run-process '(ls -l))'''
#<process xxxx "ls" active>
gosh > total xxx
-rw-r--r--   1 hoge   hoge   100 Sep  2 10:00 hoge.txt
...

環境2:Red Hat Linux release 7.2 (Enigma),gauche 0.8.9 (utf-8,pthreds)(sshでリモートログインして実行) →エラー

gosh > (use gauche.process)
#<undef>
gosh > (run-process '(ls -l))
#<process xxxx "ls" active>
gosh > exec failed: (ls -l): No such file or directory

※ターミナルからの直接コマンド実行は問題なくできます。

解決につながるような知恵をお持ちの方がいらっしゃいましたら、 お教えいただければ幸いです。 どうかよろしくお願いします。

WilikiのWriter Macro中でのcurrent-page参照

最近Wilikiでいろいろやっているものです。

早速本題ですが、WilikiのWriter Macro(define-writer-macro中)でそのマクロが書かれているページ(reader-macroならwiliki:current-page)を取得する方法はありませんでしょうか?

いろいろ試してみたのですが、失敗ばかりで悩んでおります。

よろしくお願い致します。

Schemeによる実行ファイル(.exe)の作成法

初めまして、質問させていただきます。最近(MIT-GNU)Schemeの勉強をはじめた者です。
まず、質問の内容から言うと(MIT-GNU)Schemeで実行形式ファイル(.exe)は作れるのでしょうか?
教育機関で採用されていて、あまり実用的ではないという話を聞きました。
色々検索しては見たものの実行ファイルの作成法は(自分が検索した限り)見つかりませんでした。
回答よろしくお願いします。

  1. cfで生成したファイルをまとめてリソースファイルを作る
  2. mit-schemeのload関数に相当する部分にリソースを流し込むプログラムを作る
  3. 以上のふたつとmit-schemeの必要な機能をリンクする

後者は少くとも一部はC言語などで書く必要がありますし、mit-schemeを一度はソースコードからビルドする手間をかけなければならないでしょう。その際にmit-scheme自体に手を加える必要があるかもしれません。しかし私が上で示した選択子の全てを満せます。(でもリンクしてしまうとライセンスの制約を受けてしまうかもしれません。そのあたりはあまりわかっていないので説明できませんが、注意すべき点です。) 後者はおそらく敷居が高いと思うので前者の方法をとることになると思いますが、個人的にはmit-schemeのインストールだけはあらかじめユーザーにしてもらうべきだと考えます。mit-schemeはあなたが求めているような使い方を想定していないと思います。

sys-strftimeについて

最近WiLiKiを入れてみて気付いたのですがsys-strftimeの一部処理が変なような気がするのですが。

(use srfi-19)
(sys-strftime "%T" (sys-localtime (sys-time)))
""

仕方ないのでwiliki.scmのdefault-format-timeの中の
(sys-strftime "%Y/%m/%d %T %Z" (sys-localtime time)))
の部分の%Tを%Xに書き換えて使ってます。
使ってるGaucheのバージョンは0.8.13でMingw版です。あと文字エンコーディングはShiftJISにしてあります。

http-postのバグ?

始めまして質問です。
自宅のサーバ(Anhttpd)のCGIに対してPOSTメソッドの実験をしようと思い
(http-post "hoge.co.jp" "/test.cgi" "name=hage")
というような感じでやってみたのですがCGI側の標準入力には何も入っていませんでした。
サーバかCGIの問題かなと思ったのですがPythonやnewlispで実験をすると成功するためGaucheの方を疑った訳です。
これはバグなのでしょうか?それともhttp-postの書き方を間違えているのでしょうか?
Anhttpdのtraceログを見るとメッセージボディにはちゃんとname=hageと入っているのですが。
Gaucheのバージョンは0.8.13です。http-getはちゃんと動作しました。

Shiro(2008/02/18 23:58:35 PST): http-postの第3引数に渡したデータは、 まったく加工されずにhttp POST requestのメッセージボディになります。 RFC:2616あたりをもう一回確認しないとならないんですが、通常POSTの場合 メッセージボディはmultipart/form-dataでのエンコードになってると 思います。私がhttp-postを使う時は常にmultipart/form-dataでMIMEエンコード した文字列を用意して渡しており、そうする限りでは使えています。 (Cf: RFC:1867)。

(MIMEエンコードを楽に行うライブラリルーチンをrfc.mimeにいずれ追加する 予定ですが、今のところは自分でboundaryとか生成して組み立てないとなりません)。

PythonなどでURL経由で渡されるqueryパラメータと同じ書式が許されているのは、 そのまま出しているんでしょうか。Python側で一回パーズしてmultipart/form-data にしてるってことはありませんかね。

rfc.httpはhttpクライアントプロトコルの低レベルな実装で、 将来的にはいろんなプロトコルをまとめて便利に扱えるような上位層を書くつもりなので、 http-post自体にあまり便利機能を載せる予定は今のところ無いです (認証とかpersistent connectionとかはつけるつもりですが)。

(質問者)御返答有難う御座います。忘れてましたがMingw版のGaucheです。
PythonもnewlispもContent-Typeはapplication/x-www-form-urlencodedなので
Gaucheのrfc.httpにつけてみるとか改行コードをbodyの後に追加するとかしてみましたが駄目でした。
一応multipart/form-dataにしてboundaryでbodyを囲むのも試してみましたがこれもやはり駄目。
う~ん、家のサーバかCGI側の問題なのかな?でも何故Pythonとかだと動くんだろ?
なんかメッセージボディが正しく出力されてないような気がする。

(質問者)お騒がせしました。一応解決しました。
http.scmのsend-request関数の中でsend-request-headersに与える引数の:content-lengthを:Content-Lengthに変えるだけです。
どうやらAnhttpdはヘッダの頭が小文字だと認識してくれないみたいですね。
改造したところを出しておきます。

;; send
(define (send-request out request host uri body options)
  (display #`",request ,uri HTTP/1.1\r\n" out)
  (display #`"Host: ,|host|\r\n" out)
  (case request
    ((POST PUT)
     ;; for now, we don't support chunked encoding in POST method.
(display #`"User-Agent: ,(http-user-agent)\r\n" out) ; ついでに
(display #`"Referer: http://,|host|,|uri|\r\n" out) ; 追加
     (send-request-headers (list* :Content-Length (string-size body) options) ; 変更部分
                           out)
     (display "\r\n" out)
     (display body out)
     )
    (else
     ;; requests w/o body
     (send-request-headers options out)
     (display "\r\n" out)))
  (flush out))

GLUTを対話モードで使うには?

sasagawa?(2008/01/09 04:03:01 PST): GaucheでOpenGLの使い方を勉強し始めました。REPL、対話モードから(main args)を呼び出したいのですが、argsには何を与えたらいいのでしょうか? I/O BOOKS 「GLUTによるOpenGL入門」(床井浩平著)を参考にコードを対話的に動かしながら学ぼうと思っています。

すみません、お騒がせしました。自己解決しました。(main '(""))で動きました。何かしらストリングが与えられれば良いようでした。意味はよくわからないのですが。2008/01/09 05:44:19 PST

マクロを生成するマクロでの...の扱い

齊藤(2008/01/08 04:38:30 PST): マクロを生成するマクロを書いていて躓きました。例えば以下のようなマクロを書こうとしたときにエラーになってしまいます。色々といじってみて、...の部分が外側のsyntax-rulesに対応することになっているせいではないかと思うのですが、何か回避する方法はあるのでしょうか?

(define-sytnax a
  (syntax-rules ()
    ((_ b)
     (define-syntax b
       (syntax-rules ()
         ((_ c ...)
          (begin (display c) ...)))))))

Shiro(2008/01/08 04:54:54 PST): これは実はR5RSの欠陥で、 残念ながら現在のGaucheの実装で直接回避する方法はありません。 なんとかdotted listを使う形に持って行くか、legacy macroを 使うしかないです。

R6RSではこの問題は解決され、内側のsyntax-rules内で (... ...) と書いとくと それが展開された時に... になるようになっています (11.19節)

Gaucheの開発目標としては、syntax-caseのサポートをいずれ入れる予定 なのでその時に一緒にサポートしたいと思っています。

どうしてもhygienicでやってみたい場合は、R5RS Schemeで使えるポータブルな R6RSマクロシステムがあるので試してみてください。 http://www.cs.indiana.edu/~aghuloum/r6rs-libraries/ 私は試したことが無いんですが、Gaucheでも使えるとあります。

齊藤(2008/01/08 07:18:01 PST)):いかにもありそうなケースなので「直接回避できない」というのは意外でした。すぐに実用したいというわけではないので、ライブラリや他の処理系で遊びつつ期待しております。

御紹介頂いたライブラリはライブラリというよりもr5rsで実装されたr6rs処理系というような感じで、マクロ機能だけ使うというわけにはいかないようですね。マクロというものの性質上仕方がないのかもしれませんが…。ちなみにslibのsyntax-rulesで試してみると(... ...)という書き方は使えるようですのでreplで使えるこちらの方が遊ぶには手軽かもしれません。

Shiro(2008/01/08 18:54:23 PST): 実はGaucheのR5RSマクロ実装は、macro generating macro で健全性に問題が出るというバグがあって、どうせ直すなら一緒に…などと考えている 間に時間が経っています。手元で使うときはlegacy macroでも用が足りることが多いですし。 ただ、(... ...)だけをサポートするのは多分そんなに難しくないんで、需要がなるなら やってもいいかなと思います。

condの第二形式で多値を扱う

こんにちは、doukaku.orgでいつもgaucheにはお世話になってます。さて、

gosh> (cond ((values 1 2) => (lambda (a b) (+ a b))))
*** ERROR: Compile Error: wrong number of arguments: #f requires 2, but got 1
"(stdin)":1:(cond ((values 1 2) => (lambda (a b) ...

Stack Trace:
_______________________________________
gosh>

では、できれば、3を返してもらいたいのですがどうなんでしょう。

gosh> (cond ((values 1 2) => values))
1
gosh>

とうことで、1番目の値だけがprocに渡されているようですが。(katsu)

Shiro(2007/10/23 02:55:59 PDT): condの節の最初のポジションにある式は、 評価結果が真か偽かが判断される式です。つまり(if test then else)の testにあたる部分です。なのでそこに多値が返ってきちゃうと原理的に困るんですね (condをマクロでifに展開した場合にどうなるか考えてみると困ることがわかると思います)。 なので、そこで多値を扱いたい場合はcondの節の3番目の形式を使ってください (cf. GaucheRefj:cond)。

gosh> (cond ((values 1 2) (lambda (x _) x) => +))
3

冗長にはなりますが、「どこを見て真か偽かを判定しているのか」が明確になります。 上の例では多値の最初の値で判断しているわけです。

あーそれとも、それは承知の上で第2形式でtest式が多値を返した場合に、 何らかのデフォルトの動作 (最初の値で真偽を判定する) を付け加えて欲しい、ってことでしょうか。 個人的にはここだけで多値を暗黙に単値にしちゃうのは例外を作ることになるので避けたいです。 (やるならCommon Lispのルールを全部に適用する、というような割り切りにしたいですね。 ただ、それをやってしまうとポータビリティの無いコードをあまりにうっかり書けて しまうのでちょっと恐いです。)

katsu : わかりました。私としては、成功すると多値を返し、失敗すると#f(単値)を返す手続きをpredとして、成功した場合を多引数のlambdaで受け取りたいと思っていたのですが、scheme的には、場合によって返す値の数が違う手続きは、受け取る側(condやif)が困りますよね。第三形式を活用して明示的にする方がすっきりします。ありがとうございます。

define の返す値

現在、defineはその変数「名」のシンボルを返しますが、なにか、違和感を感じます。

変数はファーストクラスオブジェクトではないので、変数そのものを返すことは出来ないけど、変数とシンボルは関係ない気がします。

返値を使うことも無いのでしょうが、すごく気になります。

返すのなら、代入された中身かなと言う気もします。

REPL的には、定義した変数が表示されて気持ちよいのだとは思いますが。

どうでも良いことのうえ、質問にもなってなくてすみません。

cygwinでのマルチバイト文字列の取扱い

以下のように環境で

>gosh -V
Gauche scheme interpreter, version 0.8.11 [sjis,pthreads]

emacsのsjisのバッファーから substring を使おうとすると

gosh> (substring "mizuho" 0 1)
"m"
gosh> (substring "みずほ" 0 1)
"\222\244\"
gosh> (substring "みずほ" 0 2)
"み"

となります。何がおかしいのか、どうやって調べれば良いのでしょうか? 2007/10/08 03:28:13 PDT

(/ 10 0) => #i1/0

 $ gosh
gosh> (gauche-version)
"0.8.11"
gosh> (/ 10 0)
#i1/0
gosh> (/. 10 0)
#i1/0
gosh>

数値演算のことが、リファレンスマニュアルを読んでもよく分からないのですが、0で割ってもエラーにならないンでしょうか。 #i1/0の意味するところがよく分からないので、教えていただけませんでしょうか。

Shiro(2007/09/16 03:25:07 PDT): #i1/0 は正の無限大です。"#i" は非正確数であることを 明示するプリフィクスです (cf. Scheme:非正確な整数)。 このような特別な数は他に負の無限大 (#i-1/0) と NaN (#i0/0) があります。 (なお、ここでの「無限大」は数学的な無限大の概念ではなくて、「計算機上で表現しきれない ほど大きな数」という意味なので、常に非正確数です。正確な無限大はGaucheにはありません。)

無限大とNaNについては、扱いと表記についてScheme界で意見が割れていたため 現在のGaucheの表記は暫定です (cf. Gauche:NaNの扱い)。 R6RSで +inf.0, -inf.0, +nan.0 が正式採用されたので、そのうちGaucheも これらに合わせるつもりです。

(/ 10 0) の扱いについては、次のふたつの解釈が有り得ます。

後者の判断は、「0.0は数学的な0を表すこともあれば正の無限小(計算機上で表現しきれない ほど絶対値が小さい数)を表現することもある」という解釈に基づきます。 IEEE浮動小数点数では負の無限小としての -0.0 も規定されているのですが、 今のところGaucheでは -0.0 は扱えません (強制的に 0.0 になる)。 R6RSでも -0.0 はオプショナルな扱いになっています。

で、どっちの解釈を採用するかは、結局使ってく上でどっちが便利かって判断になると思います。 Gaucheでは、無限大やNaNが計算途中に発生しても、計算を止めずに 最後まで結果を出してから処理を判断する方が現実的に便利と考えてこうしています。

質問者: なるほど、0の解釈にいろいろ有ることを初めて知りました。個人的には、一番C言語などの他の言語との違いを感じる部分かも知れません。div-zeroエラーが当たり前だと思っていたので。ありがとうございます。

Shiro(2007/09/16 04:38:56 PDT): C言語でも 1.0/0.0 は例外を出す処理系と IEEE浮動小数点数の無限大を返す処理系があります。 整数除算はゼロ除算例外となりますが、Cの '/' は両方が整数の場合は quotientですからね。

例外が飛んだときに、ソースコード上の位置を知りたい

実行時のエラー(例えば、invalid applicationとか)が例外としてトップレベルで報告された場合、どこで起きたかわかりません。

どの手続き中で起きるのか特定できないので、あたりを付けて、procedureに入ってすぐにguardをかけたりするぐらいしか思いつかないのですが、なにか方法はないでしょうか。

Shiro(2007/09/16 03:25:07 PDT): エラー時に出るスタックトレースが、今のところ唯一の 手がかりです (0.8.10以前のいくつかのリリースでは、スクリプトを与えて実行した 場合にスタックトレースが出ないバグがありました。0.8.11では直っています)。

ただ、スタックトレースはソースコードの見かけと必ずしも一致しません。 末尾呼び出し除去によって、f -> g -> h と呼び出しているつもりでも h 内でエラーが発生した時には f や g のスタックフレームは既に無くなっている ことがあります。これはどうしようもありません。 まだインライン展開により関数呼び出し自体が無くなっている 場合もあります (あと、現在のスタックトレース生成方法にはちょっと問題が あって、ネイティブで実装された高階関数呼び出しが間に挟まっている場合に それがトレースに表れないことがあります)。

なので今のところは #?= を適当に入れて当たりをつけてゆくしかないと思います。 将来はデバッガに入れるようにするつもりですが、だいぶん後になると思います。

質問者: 例えば、こういう例では、スタックトレースが表示されません。

(define (a x) (x x))
(print (a 0.5))
 $ gosh test.scm
gosh: "error": invalid application: (0.5 0.5)
 $

0.8.11です。このコード例であればどこかは明確なのですが。

Shiro(2007/09/16 04:47:47 PDT): あれ…? ああ、わかりました。0.8.11でスタックトレースが出ないバグを直したつもりだったのが 漏れがありました。main実行開始後のエラーについては対応したけれど、main実行 開始前のがそのままでした。

とりあえず、可能ならトップレベルでの計算を避けて、関数に入れてみてください。 (マクロで計算してるとそれができない場合もあるんですが…)

質問者: mainを経由して実行すると、スタックトレースが出るようになりました。できるだけmain経由にします。ありがとうございました。

cygwinでのインストール

Gauche:Cygwinを読んだりGoogleで検索したりしましたが、 同様の不具合例が無かったのでこちらで質問させて下さい。 (基本的すぎて誰も悩んでいないんだろうな…, j-kon)

下記の環境でGauche-0.8.11をmakeしようとした所、 (1度もGaucheをインストールした事が無いPCです。)

以下のようなエラーが出ました。

make[1]: Leaving directory `/Gauche-0.8.11/ext'
make[1]: Entering directory `/Gauche-0.8.11/doc'
../src/gosh -q -I../src -I../lib -lgauche-init ./extract -en -o gauche-refe.texi gauche-ref.texi
gosh: "error": Compile Error: Compile Error: cannot find file "srfi-1.scm" in *load-path* ("../lib" "../src" "/usr/local/share/gauche/site/lib" "/usr/local/share/gauche/0.8.11/lib")
"../lib/gauche/parseopt.scm":36:(define-module gauche.parseopt (use  ...

"././extract":18:(use gauche.parseopt)

make[1]: *** [gauche-refe.texi] Error 1
make[1]: Leaving directory `/Gauche-0.8.11/doc'
make: *** [all] Error 2

アドバイスを頂けると幸いです。

    1. *.scm と *.dll のパーミッションを手で直しつつ
    2. シンボリックリンクも *.lnk となっているものは手で直す

デバッグの方法について

初心者向けに移動しました。yamasushi

More ...