Scheme:初心者の質問箱:log01
- carを循環させたリストをdisplayするとSegmentation fault
- variable capture in gauche.test ?
- WiLiKiがqdbmで動作しない
- 変数名を値とする文字列から変数の値を得る方法
- インスタンスを作成するマクロを書く場合のクォートの仕方について
- モジュール・マクロ・ eval についての質問
- 例外と継続
- リストの構築
- define と set!
- object-apply を以前の値に戻す方法
- parse-options (gauche.parseopt) についての質問
- 外部プロセス呼び出しができない
- WilikiのWriter Macro中でのcurrent-page参照
- Schemeによる実行ファイル(.exe)の作成法
- sys-strftimeについて
- http-postのバグ?
- GLUTを対話モードで使うには?
- マクロを生成するマクロでの...の扱い
- condの第二形式で多値を扱う
- define の返す値
- cygwinでのマルチバイト文字列の取扱い
- (/ 10 0) => #i1/0
- 例外が飛んだときに、ソースコード上の位置を知りたい
- cygwinでのインストール
- デバッグの方法について
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)
- 齊藤(2009/08/08 11:44:33 PDT): そういうものです。 マニュアルの write 及び display の項目を見て下さい。 「objが循環する構造を持っていた場合、これらの手続きは停止しないかもしれません。write/ssを参照して下さい。」と注意書きがあります。 http://practical-scheme.net/gauche/man/?l=jp&p=write
- 齊藤(2009/08/08 11:51:44 PDT): スタックが溢れるかもしれないとは思いますが、その場合は Segmentation fault になるものでしょうか? やっぱりバグ? いずれにしても意味のある挙動は期待でません。
- teppey(2009/08/09 01:44:56 PDT): たしかに、Segmentation faultしなくても出力され続けるだけなので、あまり意味のあるプログラムではないですね。cdrを循環させたリストでは停止しないので、carでSegmentation faultするのはなぜ、と考えました。
- 齊藤(2009/08/09 03:16:22 PDT): コードを見ずに推測ですが、cdr 側は末尾再帰になってるんじゃないですかね。
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) にしたら問題ないようでした。 使い方の問題か、バグか分かりませんが、とりあえず。
- 齊藤 (2009/07/15 06:09:24 PDT) test を上書きしてしまっているので良い使い方とは言えないと思いますけど…。 test というのがありがちな変数名であるのも確かです。 define-macro だと展開時のモジュールを参照してしまうようなので、ここは define-syntax で定義した方が良いケースかもしれないですね。
(define-syntax test* (syntax-rules () ((_ msg expect form compare ...) (test msg expect (lambda () form) compare ...))))
- Shiro(2009/07/15 06:29:57 PDT): 非健全なマクロの落とし穴ですね。これはgauche.testの
バグと言って良いかと (同じ問題は
lambdaを再束縛していても生じます。健全なマクロを使っていればtestやlambdaの
再束縛には影響を受けません)。
で、なんでtest*にdefine-syntaxを使わなかったのか、もう覚えてないんですが、 もしかすると健全なマクロが出来る前に書いたからかも。
WiLiKiがqdbmで動作しない
cudo(2009/03/25 23:35:13 PDT)WiLiKiがgdbmでは動作しますが、qdbmでは動作しません。
単に新規サイトを作るだけならgdbmでもいいのですが、既存のWiLiKiサイトの移行を考えておりそのサイトがqdbmで動作しているため、qdbmで動作させたいと考えています。
Gauche、qdbm、WiLiKiのバージョンは以下の通りです。
- Gauche:0.8.14
- qdbm:1.8.77
- Gauche-qdbm:0.2
- WiLiKi:0.6.1
/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>
以上、何か解決方法をお教えいただけたら幸いです。よろしくお願いします。
- Shiro(2009/03/26 00:37:47 PDT): パス等は正しいようなので、/usr/share/gauche/site/lib/dbm/qdbm.scm のパーミッションかなあ。このファイル、もしくはここに至るパスが cgiの動作権限で読めるようになっているかどうかをまず確かめてみてください。
- cudo(2009/03/26 19:19:17 PDT): 早速のご回答ありがとうございます。ファイルとディレクトリdbmのモード、所有者、グループを確認したところ、
qdbm.scm:444、root、root
dbm:755、root、root
となっていました。これは正常動作するgdbm.scmについても全く同じでした。 念のため qdbm.scm をgdbmと同じ /usr/share/gauche/0.8.14/lib/dbm/ にコピーしてみたらなぜか動作するようになりました。ありがとうございました。何が良くなかったのはいまいち釈然としませんが…。
変数名を値とする文字列から変数の値を得る方法
- osn(2009/03/03 17:12:39 PST)
とても基本的なことで恐縮ですが、
gosh> (let ((a 1) (b "a")) (string->symbol b)) a
と、このとき、a が返ってくるのはわかるのですが、変数 a の値の 1 を返すにはどうしたらいいでしょうか。 - 齊藤(2009/03/03 18:43:43 PST): 変数 b の内容からローカルな a を探すということですか? 出来ません。 以前に似たような出ているようなので参考にして下さい。 http://practical-scheme.net/wiliki/wiliki.cgi?hira%3a%E8%B3%AA%E5%95%8F%E7%AE%B1#H-1r8rqsm
- osn(2009/03/03 19:40:48 PST) ローカルだからできない、ということで
gosh> (define a 1) a gosh> (define c 3) c gosh> (let ((v "a")) (eval (string->symbol v) (interaction-environment))) 1 gosh> (let ((v "c")) (eval (string->symbol v) (interaction-environment))) 3 gosh> (set! c 4) 4 gosh> (let ((v "c")) (eval (string->symbol v) (interaction-environment))) 4
なるほど。。。。実行時にはローカル変数の"名前"は存在しないのでいかんともしがたい、んですね。ありがとうございました。
インスタンスを作成するマクロを書く場合のクォートの仕方について
あらかじめ定義したクラス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))))
- 2008/11/12 23:37:12 PST 本来意図していたコード例は、ご指摘の通りです。埋め込み言語(でいいのでしょうか?)といわれるものが、いまひとつぴんとこなかったので、こんなことをごちゃごちゃ試そうとしていたのでした。回答ありがとうございました。
モジュール・マクロ・ eval についての質問
- Theoria? (2008/11/10 22:21:23 PST)
あるモジュールで定義されたマクロにおいて、別のモジュールから利用される際に、マクロ展開時に eval するモジュールをどのように選ぶべきか解りません。具体的には次のコードで proc-in-user は unbound variable になります。
(define-module x (define-macro (foo x) ((eval x (current-module)) 0 1 2)) (export foo)) (select-module user) (import x) (define proc-in-user +) (foo (lambda (x y z) (/ (proc-in-user x y) z)))
ここで eval の第二引数に「呼び出し元モジュール(この例では user )」を指定できると、 proc-in-user が利用できます。そうすると、大変助かるのですが、そのような方法はないものでしょうか?
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")))
- Shiro(2008/11/07 03:19:27 PST): 継続を使う必要はありません。これでok:
(guard (e (else #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 (mk ls) (define (rec ls r nested?) (cond ((null? ls) (values '() (reverse r) nested?)) ((eq? #\( (car ls)) (receive (xs rs err) (rec (cdr ls) '() #t) (if err (values '() '() err) (rec xs (cons rs r) nested?)))) ((eq? #\) (car ls)) (values (cdr ls) (reverse r) (not nested?))) (else (rec (cdr ls) (cons (car ls) r) nested?)))) (receive (xs rs err) (rec ls '() #f) (if err 'error rs)))
今回はじめてmatchを知ったのですが、これは便利だと思いました。継続渡し形式、プッシュダウンオートマトンについても勉強してみます。
define と set!
osn(2008/09/24 23:36:38 PDT): (define a ..) では、記憶域に .. が確保されて、そこのアドレスが a に関連づけられ、(set! a ....) では、a が差すアドレスはそのままで、記憶域の内容が .... に変更される、という理解でいいんでしょうか。
- osn(2008/09/25 03:07:47 PDT): 唐突に漠然としたこと書いてすいません。上記のように理解していたのですが、(define a 1) (set! a 2) は許されて、(define a (list 1 2 3)) (set-car! a 3) は許されないのはなぜ、等、悩んでしまいまして。プリミティブ型を define する時は変数にはアドレスでなく値そのものが納められる?だからOK?等想像してますが、逆に破壊的変更の使い方(使用上の注意)ってないのかしらん(アドレスの変更にあたる操作しかしてはいけない?)等、今まで、結構安易に set! を使ってしまっているのが気になりだしてしまい、、、
Shiro(2008/09/25 03:33:25 PDT): 「アドレス」というのを考えなくていいと思います。
- (define a <expr>) では <expr>を評価した値に a というラベルをつけて 後からaで参照できるようにする
- (set! a <expr2>) ではラベルaを<expr2>の結果の値へと貼り替える
もちろん、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宣言されてるデータ) を変えようとするからです。)
- 齊藤(2008/09/25 04:06:46 PDT): osn さんの説明は Scheme の世界を他の概念に置き換えたものになっているので、どこが間違っていると説明しにくいですね。 言いたいことはわかるのですが、他言語 (あるいは言語の実装) の機構に置きかえて理解しようとするとどうしてもそちらの概念にひきずられます。 特に Scheme は様々な機能を抽象化された存在として提供しているので、その概念を掴まずに中身がどうなっているのかから理解しようとしても混乱するばかりだと思います。 抽象化されたものを扱うのに慣れていないのかもしれませんね。
- osn(2008/09/25 05:56:19 PDT): 齋藤さん、お言葉、耳が痛うございます。概念を掴もうともがいている過程と御容赦くださいませ。
osn(2008/09/25 05:56:19 PDT): Shiro さん、コメントありがとうございました。
- (set! a <expr2>) ではラベルaを<expr2>の結果の値へと貼り替える
を
- ラベルaを貼った先の中身を入れ換える
というふうに完全に思い違いしてました。
- osn(2008/09/25 16:58:35 PDT): なぜ上記のように誤解したのかちょっと思い返してましたら、例えば、R5RS でも set! の説明で
(set! <変数> <式>) <式> が評価され,<変数> が束縛されている場所に結果の値が格納される。
とあって、”<変数> が束縛されている場所(=メモリアドレス)に値を格納(=代入)”と受け取ったからでした。 変数に値を”代入”という言い方は実は必ずしも適切ではなくて、 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 となって、前者はいいとして、後者はなぜ? と悩み出したのが遠い発端なんですが、これはどう理解したらいいでしょうか。
- 齊藤(2008/09/25 07:41:09 PDT): 後者は R5RS では規定されていない振舞いです。 Gauche でも数値同士の比較に必ず eq? が使えるわけではありません。 まず、 eq? は内部的にはポインタ (メモリアドレス) の比較として実装されることが多いです。 そして、小さな値は効率化のために別個にメモリを割当てずに、変数の中に直接入れてしまうという実装をすることがあります。 Gauche でもたぶんそうなってます。 なので、 eq? で比較するとアドレスでなく数値を直接比較することになり、 #t を返すことになるのだと思います。 例えば 1 でなく巨大な値で試すと別個にメモリを割当ててそのアドレスを比較する形になるので #f が返されます。 あくまで、実装上の都合でたまたまそうなっているだけです。 Scheme の言語の世界で話をするなら未定義です。
- 齊藤(2008/09/25 07:50:39 PDT): R5RS によると前者も未定義ですね。
- osn(2008/09/25 16:44:52 PDT): コメントありがとうございました。なるほど、Gauche のユーザリファレンスや R5RS を見返してみると言われる通りですね。変数を理解したくて、アドレスを比較しているであろう eq? の挙動を考える材料にしたつもりだったのですが、そのアプローチが的外れだったようです。
object-apply を以前の値に戻す方法
osn(2008/09/22 05:43:33 PDT):(define-method object-apply ... )を行って、その後、元の定義に戻すにはどうしたらいいでしょうか。define-method 実行前の状態を取り出して保存しておいて後でその状態で再定義する、あるいは、デフォルトの定義に戻すということができれば、と思っています。
- 齊藤(2008/09/22 07:37:59 PDT) : あらかじめ (define old-method (ref object-apply 'methods)) とでもして元の定義を保管しておき、あとで (set! (ref object-apply 'methods) old-method) とすれば元に戻せるようです。 これが作法にかなうものかどうかはわかりませんが…。
- osn(2008/09/23 06:52:42 PDT): ありがとうございました。(set! ...) の方がよくわからなかったのですが、いただいたコメントを参考に
(define old (slot-ref object-apply 'methods)) (define-method object-apply ....) (slot-set! object-apply 'methods old)
で元に戻せてることを確認しました。- osn(2008/09/23 22:12:30 PDT):(set! ...) でもいけました。set! は Special Form で、第一引数はアドレスが、第二引数は値が参照される、ということですね。
- 齊藤(2008/09/23 22:19:37 PDT):アドレスという理解は誤りです。 「一般化された set!」を読んで下さい。 簡単に説明すると ref には見えない手続きがくっついていて、実際にはそれが呼ばれる、という感じでしょうか。 今回の場合は最終的に slot-set! にディスパッチされるはずです。
- osn(2008/09/23 22:44:49 PDT):解釈が乱暴すぎました。ありがとうございました。
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 の中で処理できるようになるかなぁ、と。そう変更して、嬉しい場合はあっても、デメリットはあまりないのでは、と期待してます。
- Shiro(2008/09/08 09:56:31 PDT): どちらかというと、let-argsで処理してもらう 方向を考えていました。parse-optionsはビルディングブロックという感じです。 let-argsよりparse-optionsの方がいい場面ってありますか?
- osn(2008/09/09 00:14:31 PDT):あ、実際は let-args を使っています。でも、let-args だと上記の状
況をうまく処理できるんでしょうか?
(let-args (cdr args) ((i "i=s") (else (xxx args continue) (print xxx args) (continue (cdr args)))) (print "rslt:" i))
に対して、$ ./ttt.scm -i aaa ccc rslt:aaa
となり、ccc が args の先頭に来たときパースが中断されていると思いますが、else 節で捕まえられるとその中で処理できるのにぃ、ということなんですけども。なにか見過ごしてますでしょうか。
- Shiro(2008/09/09 00:42:13 PDT): こういうふうに処理するんでは都合が悪い、
ということがありますか?
(let-args (cdr args) ((i "i=s") . files) (print "files: " files))
あ、もしかして、gnu getopt_longみたいにファイル名のあとに再びオプションが現れることを 許したいということでしょうか。それならgetopt_longと同様、argsの方を並べ替えて しまう方が綺麗かなあ。個人的には、オプションとそれ以外を混ぜるのを許すと オプション引数の与え間違いが検出しにくくなるのであんまり嬉しくないかなあと 思ってますが。
- osn(2008/09/09 04:42:57 PDT):失礼しました。ドキュメントの [. rest] を意味がわからないまま無視してしまってました。ただ、ファイル名とオプションの混在可はわたしも特に必要とは思わないですが、先にファイル名を羅列して、そのあとにオプションを並べたいことはあるかも、とちょっと思ったりします。まぁ、その場合向けには、引数リストの先頭からファイル名の羅列を切り離せばいいので、特に問題ではありませんね。ありがとうございま した。
外部プロセス呼び出しができない
初めて書き込みさせていただきます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
※ターミナルからの直接コマンド実行は問題なくできます。
解決につながるような知恵をお持ちの方がいらっしゃいましたら、 お教えいただければ幸いです。 どうかよろしくお願いします。
- Shiro(2008/09/01 23:40:18 PDT): 単にGaucheのバージョンが古いだけ、
という気がします。
run-processがリストを取れるようになったのはわりと最近です
(以前はSTkに合わせた仕様で、(run-process "ls" "-l") みたいに使っていた)。
RedHatの方で0.8.13をコンパイルして試してみてください。
- cudo(2008/09/02 19:52:21 PDT):早速のご回答ありがとうございます。0.8.13のインストールを試みましたが、makeでエラーが出てしまいました(下記の通り、パス部分だけ一部~に置き換えています)。自分でも原因と解決法を調査中ですが、もし何かご存じでしたら教えて下さい。よろしくお願いします。
... make[2]: 入ります ディレクトリ `~/Gauche-0.8.13/ext/text' ../../src/gosh -ftest ../../src/gencomp --ext-module text/gettext.scm ./text-gettext-lib.scm Error in compiling (use gauche.charconv) Error in compiling (define-module text.gettext (use srfi-1) (use util.list) (use srfi-13) (use rfc.822) (use file.util) (use binary.io) (use gauche.charconv) (use util.combinations) (export gettext textdomain dgettext dcgettext bindtextdomain ngettext make-gettext)) *** ERROR: Compile Error: failed to link "libcharconv" dynamically: libiconv.so.2: cannot open shared object file: No such file or directory Stack Trace: _______________________________________ 0 (eval `(use ,mod) (compile-module)) [unknown location] 1 fn 2 (port-fold compile-toplevel-form '() read) At line 145 of "./../../src/gencomp" 3 (with-input-from-file src (lambda () (emit-toplevel-executor (reve ... At line 142 of "./../../src/gencomp" 4 (do-it scmfile (or output-base (sys-basename (path-sans-extension ... At line 118 of "./../../src/gencomp" make[2]: *** [text-gettext-lib.c] エラー 70 make[2]: 出ます ディレクトリ `~/Gauche-0.8.13/ext/text' make[1]: *** [all] エラー 2 make[1]: 出ます ディレクトリ `~/Gauche-0.8.13/ext' make[1]: 入ります ディレクトリ `~/Gauche-0.8.13/doc' ../src/gosh -q -I../src -I../lib -lgauche-init ./extract -en -o gauche-refe.texi gauche-ref.texi gosh: "error": Compile Error: failed to link "libcharconv" dynamically: libiconv.so.2: cannot open shared object file: No such file or directory "././extract":19:(use gauche.charconv) make[1]: *** [gauche-refe.texi] エラー 1 make[1]: 出ます ディレクトリ `~/Gauche-0.8.13/doc' make: *** [all] エラー 2
- cudo(2008/09/02 19:52:21 PDT):早速のご回答ありがとうございます。0.8.13のインストールを試みましたが、makeでエラーが出てしまいました(下記の通り、パス部分だけ一部~に置き換えています)。自分でも原因と解決法を調査中ですが、もし何かご存じでしたら教えて下さい。よろしくお願いします。
- Shiro(2008/09/02 21:34:03 PDT): エラーメッセージが言う通り、libiconvが見つからないってことです
(libiconvは文字コード変換を行うライブラリ)。
最近のLinuxではiconvの機能はlibcに標準で含まれるようになっていて、
libiconvは必要ないはずなのですが、
Red Hat 7.2というのは相当古いシステムなのでlibiconvをリンクしに行ってしまうようです。
OSをアップデートできない何らかの事情があるのであれば、
別途libiconvをインストールしてください。
http://www.gnu.org/software/libiconv/
Gaucheのソースツリー中のINSTALLファイルの "Choosing Character Encoding" の 項も読んでください。 (ただ、RH7.2というのはいくら何でも古すぎると思います。 もう標準でのセキュリティパッチとか提供されていないと思うんですが。 マニュアルでまめにソフトをアップデートしていないのなら セキュリティ上の脆弱性がそのままになってると思うので、 なるべくOSの入れ替えを考えた方が良いかと。) - cudo(2008/09/03 02:04:17 PDT):丁寧にご指摘ありがとうございます。
とりあえずlibiconvを入れてやってみます。
OS入れ替えについても検討します。UNIXには明るくないので、少し時間がかかりそうですが…。- cudo(2008/09/04 22:50:35 PDT):既にlibiconvは入っていて所定の場所にファイルは存在していました(gaucheから呼び出したい外部プログラムのインストールの際、前提としてlibiconvが必要でインストールしていました)。 "--with-iconv=/usr/local" オプションを付けて./configureしていましたが、なぜかうまく行きません。現在、OS入れ替えと並行で調査中です。
WilikiのWriter Macro中でのcurrent-page参照
最近Wilikiでいろいろやっているものです。
早速本題ですが、WilikiのWriter Macro(define-writer-macro中)でそのマクロが書かれているページ(reader-macroならwiliki:current-page)を取得する方法はありませんでしょうか?
いろいろ試してみたのですが、失敗ばかりで悩んでおります。
よろしくお願い致します。
- Shiro(2008/07/15 03:31:23 PDT): 現在のWiLiKiのコードそのままでは出来ないっぽいです。
次のパッチを試してみてください。
--- src/wiliki/edit.scm (revision 394) +++ src/wiliki/edit.scm (working copy) @@ -27,6 +27,7 @@ ;;; (define-module wiliki.edit + (use gauche.parameter) (use srfi-13) (use text.gettext) (use text.diff) @@ -161,7 +162,9 @@ (define (update-page content) (when (page-changed? content (ref p 'content)) - (let1 new-content (expand-writer-macros content) + (let1 new-content + (parameterize ([wiliki:page-stack (list p)]) + (expand-writer-macros content)) (write-log (wiliki) pagename (ref p 'content) new-content now logmsg) (set! (ref p 'mtime) now) (set! (ref p 'content) new-content)
- さっそくのご回答ありがとうございます。上記のパッチをあて、下記のマクロを試したところ
(define-writer-macro (testmacro) (list (x->string (wiliki:current-page))))
以下のように展開されました#f
使用方法が間違っているでしょうか?
- Shiro(2008/07/20 09:31:54 PDT) うーん、私の手元だと上のtestmacroで
#<<wiliki-page> 0x955be58>
と表示されます。svn trunkの最新版を使ってることと、それが確かに インストールされていることを確認してください。(svn trunkには上のパッチが 既に反映してあります)
Schemeによる実行ファイル(.exe)の作成法
初めまして、質問させていただきます。最近(MIT-GNU)Schemeの勉強をはじめた者です。
まず、質問の内容から言うと(MIT-GNU)Schemeで実行形式ファイル(.exe)は作れるのでしょうか?
教育機関で採用されていて、あまり実用的ではないという話を聞きました。
色々検索しては見たものの実行ファイルの作成法は(自分が検索した限り)見つかりませんでした。
回答よろしくお願いします。
- yokota(2008/04/07 22:45:54 PDT): 特に理由がなければ MIT/GNU Scheme にこだわるよりは最初から実行形式が作成できる Scheme を使ってみてはいかがでしょうか。CHICKEN は実行形式を作成できます。
- かーみ? (質問者)です。回答ありがとうございました。
それは、MIT-GNU Schemeでは絶対に実行形式は作成できないと言うことなのでしょうか?
Chikenは、もちろんダウンロードしました。ですが、ネット上にあまり日本語の資料がなかったため
実行形式の作り方がわかりませんでした。
重ね重ねすいませんが回答よろしくお願いします。 - Shiro(2008/04/08 10:05:44 PDT): 「絶対に~ない」ことを答えるのは悪魔の証明みたいな
もので難しいですよ。世界のどこかで誰かがこっそりexe化プログラムを作ってるかも
しれませんし。
私はMIT Schemeを常用してないので一般的な話しかできませんが、 そもそもexeにしたいという目的は何でしょう? コンパイルによる性能向上が 目的なら既にバイナリを生成する機能がMIT Schemeにあるので (マニュアルの"Compiling prorams"の章参照)、 それと何らかのバッチファイルの組み合わせでいけるでしょう。 形式上どうしても実行ファイルがexeになってないと困る事情があるのならば、 Gauche:scm2exeに似た手段が使えるかもしれません。 「ひとつのexeファイルをコピーすればどこでも動く」ということを実現したいとすると、 かなり別の工夫が必要になるでしょう (たとえ処理系がexeにする手段を提供していた としても、ランタイムライブラリは別にインストールしなくちゃならないというケースも 多いと思うので)。
- かーみ? (質問者)です。回答ありがとうございます。
すいません、exeにこだわってはいませんでした。実行ファイルであればなんでもOKでした。
(exeに限らない)実行ファイルをMIT Schemeで作るにはここらへんを参考にすればいいのはShiroさんのリンクによりわかったのですが、
翻訳してみてもあまり意味がわかりません。cfでソースファイルを参照して、sfで作る、といった感じだと思ったのですが
間違っていました。実行ファイルを作成(バイナリを生成??)するにはどのようにすればいいのでしょうか? - 齊藤 (2008/04/09 05:37:52 PDT): 日本語訳があります。
http://www.ueda.info.waseda.ac.jp/~ichibe/pub/members.tripod.co.jp/zzyyb/mit/doc-ja/user_5.html#SEC34
cf関数でネイティブコードにコンパイルし、それをloadで呼び出すことができる、ということです。
ですが、質問の内容が不明確です。求めている「実行ファイル」とは何ですか?「(バイナリを生成??)」というように、自分の目的の説明にハテナマークがついているところからして、自分がやりたいことが既にわからなくなって混乱しているようにも思います。 shiroさんも述べている通り、まずどうして実行ファイルを欲しいのかを一旦説明すれば妥当な回答が得られやすいかと思います。
- かーみ? (質問者)です。誘導ありがとうございます。リンクも参考になりました。
Schemeで作ったソフトウェアを公開したいと考えております。なので、実行ファイルが作りたいのです。
説明不足ですいませんでした。これで私の意図が伝われば幸いです。
回答よろしくお願いします。 - 齊藤(2008/04/09 09:22:00 PDT):公開したいというだけでは情報として不足です。公開することと実行ファイルを作成することの間に関連性がありません。説明が苦手なようなのでこちらで勝手に意図を推測して選択肢を書きます。この中に該当するものがありますか?
- ユーザーにmit-schemeをインストールさせる手間をかけさせることなく実行できるようにしたい
- 実行できる形式で配布したいがソースコードは非公開としたい
- 公開するにあたってなるべく高速に実行させたい
- 1個のexeファイルにまとめたい
- かーみ? (質問者)です。誘導ありがとうございます。選択肢まで作っていただき、本当にありがとうございます。
該当するのは、- ユーザーにmit-schemeをインストールさせる手間をかけさせることなく実行できるようにしたい
- 実行できる形式で配布したいがソースコードは非公開としたい
の二つです。二つを選択すると言うのがおかしいことであれば、一番目だけだと思います。
とにかく配布したとして、その実行ファイルをダブルクリックするだけで実行できるようにしたいです。
少なくとも高速に実行させたり、1個のexeファイルのまとめたいということではないです。
回答よろしくお願いします。
- 齊藤(2008/04/10 06:29:28 PDT):私が思い付く方法としては2種類あります。
まずひとつはあなたが公開したいと考えているソフトウェアのインストールパッケージを作り、mit-schemeも一緒に入れてしまうという方法です。cfで生成したものをloadさせるバッチファイルを作っておけばそれをダブルクリックするだけで起動できます。(上でshiroさんが提示している方法ですね。) ただ、mit-scheme全体は結構大きいので配布する際には不要そうな部分を削るといった工夫が必要でしょう。
もうひとつはまさにexeを作る方法です。手順を説明しましょう。私自身はmit-schemeをインストールしたこともなく具体的に把握しているわけではありませんが、おおまかにはこんな感じで出来るはずです。
- cfで生成したファイルをまとめてリソースファイルを作る
- mit-schemeのload関数に相当する部分にリソースを流し込むプログラムを作る
- 以上のふたつと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にしてあります。
- yokota(2008/03/10 10:27:14 PDT): sys-strftime はそのプラットフォームのCライブラリのstrftime()の機能に依存します。MinGWではCライブラリとしてMSVCRTを使っており、これは"%T"をサポートしていません。そのため、C言語で同じ事をするプログラムを書くと同じような問題が起きます。この場合は SRFI-19 を使っているので SRFI-19 の機能を生かして以下のようにすると良いでしょう。
(use srfi-19) (date->string (current-date) "~3")
- (質問者) Mingw版では仕方が無いのですね。回答有難う御座いました。
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はちゃんと動作しました。
- 齊藤(2008/02/18 20:30:15 PST)私が確認したところではとりたてて異常なところは見られませんが…。 例えば (http-post "practical-scheme.net" "/wiliki/wiliki.cgi" "c=rss") としてrssが返ってくるかどうかを確認してみてはどうでしょう。 それと、私自身は確認していませんが、VC版のGaucheではソケット関連でまだ問題があるという話もあるので、使っているGaucheのコンパイル環境も書いた方がよろしいかと思います。
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とかだと動くんだろ?
なんかメッセージボディが正しく出力されてないような気がする。
- 齊藤(2008/02/19 08:53:15 PST): 試したCGIも出してもらえればもっと調べてみることができるかもしれません。
(質問者)お騒がせしました。一応解決しました。
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))
- Shiro (2008/02/20 03:43:23 PST): RFC:2616 section 4.2に
"Field names are case-insensitive. " と明記してあるので、
httpd側で直してもらえたらありがたいですね。
あと「ついで」の部分ですが、refererはそれじゃ意味が違うのでは? refererを知っているのはcaller側しかないので、明示的にhttp-postに 与えてもらうしかないと思います。
user-agentはデフォルトで付加してもいいかも。callerがオーバライド したい場合はオプションを与えるってことで。
- (質問者) あ~そうですね。家の掲示板はリファラがないと投稿を受け付けないんで適当につけちゃってました^^
一応Anhttpdの作者の所に報告はしましたけど、はたして次のバージョン出るのかしら?
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的には、定義した変数が表示されて気持ちよいのだとは思いますが。
どうでも良いことのうえ、質問にもなってなくてすみません。
- (nobsun):何か値を返すということ自身についてはどう思われます? (define foo bar)は何か値を返すべき式なのかそれとも別ものだと思いますか? 答でなくて、質問になっちゃってごめんなさい。。。
- (質問者)#<undef>で良い、というか、#<undef>の方が適切に思えます。しかし、シンボルが返るのにもにも理由があるのか、納得できればそれはそれで
- Shiro(2007/10/09 13:17:18 PDT): 特に深い理由はありません。 強いて言えばCommon Lispのdefunが定義された名前を返すので、 なんとなくそれに倣ったという感じでしょうか。 defineはシンボルを受け取って、環境内の「シンボル→値」という束縛を 変更しているので、シンボルは全く関係無いというわけではないかと。 #<undef>もリーズナブルな選択ではあるでしょうね。
- (質問者)自分で、少し考えてみました。まとまりが無くてすみません。実用上なんの問題もない勝手な拘りです。これ書きっぱなしで終わりにします。ありがとうございます。
- R5RSには特に記述無い。
- set!は、束縛する値を返す。defineとの違いは?
- defineがletrecさらに、lambdaに変換できるということを考えると、束縛する時点で値が返ることはない。(束縛という行為(?)を式の中で利用することは出来ない)
- special formなんだから、あんまり気にしすぎないのが良い。
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
- 齊藤(2007/10/08 03:36:46 PDT): emacsの設定はset-buffer-file-coding-systemでバッファのエンコーディングを指定したのでしょうか。set-buffer-process-coding-systemでgoshプロセスのエンコーディングも指定しておく必要があるんじゃないかと思います。
(/ 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です。)
- OS : Windows XP, Version 2002, Service Pack 2
- Cygwin : PostScript関係とtetex関係を除いてフルインストールしています。
- GCC : 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
- Configure : ./configure --enable-multibyte=sjis
以下のようなエラーが出ました。
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
アドバイスを頂けると幸いです。
- isi(2007/09/11 08:45:52 PDT): ごく普通にcygwinを使っていると思っている者ですが、このようなエラーに遭遇したことはありません。あのー、上のmakeの出力の、上から2行目と下から2行目のディレクトリのパス名が違うじゃないですか。これは、この通りに表示されたんでしょうか? 同じパスが表示されると思うのだけど。何か作業過程でディレクトリの移動とか、シンボリック・リンクを使っているとか、特徴的な事がないでしょうか。 このメッセージの意味は、/Gauche-0.8.11/doc にある ./extract というスクリプトを起動したんだけど、../lib/srfi-1.scm が見つからなかったってことなので、何かこの辺が怪しいのではないかと思います。はずしてたらごめんなさい。
- j-kon(2007/09/11 15:45:45 PDT): パス名が違うのは手動sed漏れです。失礼しました。yasuyukisさんはCVS HEADで同じエラーが出たみたいですが、原因は同じなのかなー? srfi-1.scm は /Gauche-0.8.11/libsrc にあるんですが。
- Shiro(2007/09/11 17:39:09 PDT): まず、../lib/srfi-1.scm がありますか? 無かった場合、それ以前のビルドが失敗している可能性が高いです。makeの出力を良く調べてみてください。lib/srfi-1.scmは次のような段階を経て生成されます。
- libsrc/srfi-1.scm をソースとして、ext/srfi/ 以下に srfi-1-lib.dll と srfi-1.scm が生成される
- lib/srfi-1.scm が ext/srfi-1/srfi-1.scm を指すシンボリックリンクとして生成される
- 従って、まずext/srfi以下のビルドが成功してるかどうかを確認してください。失敗しているようならそのエラーメッセージなどをお願いします。
- j-kon(2007/09/11 20:31:26 PDT): お手数かけます。早く自己解決できるようにならねば!!
- lib/srfi-1.scm はありません。
- makeの出力を確認すると、Shiroさんのおっしゃる通り ext/srfi のビルドに失敗しています。
make[2]: Entering directory `/Gauche-0.8.11/ext/srfi' ../..//src/gauche-config --fixup-extension srfi-1 gcc -DHAVE_CONFIG_H -I. -I../../src -I../../gc/include -g -O2 -fomit-frame-pointer -march=i686 -DUSE_I686_PREFETCH -c srfi-1_head.c ../..//src/gosh -ftest gencomp --ext-module srfi-1.scm -o srfi-1-lib ../../libsrc/srfi-1.scm gosh: "error": Compile Error: Compile Error: Compile Error: Compile Error: failed to link "gauche-collection-lib" dynamically: Permission denied "../../lib/gauche/sequence.scm":1:(define-module gauche.sequence (use ... "../../lib/gauche/cgen/unit.scm":36:(define-module gauche.cgen.unit (use ... "../../lib/gauche/cgen.scm":45:(define-module gauche.cgen (extend g ... "../../src/gencomp":48:(use gauche.cgen) make[2]: *** [srfi-1-lib.c] Error 1
- 生成される *.dll のパーミッションをひとつひとつフルコントロールにしながらmakeを繰返した所、ext/srfi/ 以下に srfi-1-lib.dll と srfi-1.scm が生成され、lib/srfi-1.scm も ext/srfi-1/srfi-1.scm を指すシンボリックリンクとして生成されましたが、頭書と同様のエラーが出力されました。御参考までに最後のmakeの全出力をコメントとして貼っておきます。
- Shiro(2007/09/11 21:19:40 PDT) うーん、lib/srfi-1.scmが出来てるのにこのエラーは
奇妙ですね (シンボリックリンクがちゃんと機能しているのを確認するため、
cat lib/srfi-1.scm でちゃんと内容が表示されるかどうか確かめてください)。
dllのロードがpermission deniedになるってことは、最近のcygwinで何か パーミッション関係が変わったのかな? gccはうちのバージョンと同じですが…
試しにsrc/ に降りて、./gosh を -ftest -fload-verbose オプションで起動し、 (use srfi-1)してみてください。うちの環境だとこうなります。$ ./gosh -ftest -fload-verbose ;;Loading ../src/gauche-init.scm... ;;Loading ../lib/gauche/interactive.scm... gosh> (use srfi-1) ;;Loading ../lib/srfi-1.scm... ;; Dynamically Loading ../src/srfi-1-lib.dll... #<undef>
- j-kon(2007/09/11 22:53:58 PDT): こんな感じです。
>cat lib/srfi-1.scm cat: lib/srfi-1.scm: No such file or directory >ls lib/srfi-1.scm* lib/srfi-1.scm.lnk >cd src >./gosh -ftest -fload-verbose ;;Loading ../src/gauche-init.scm... ;;Loading ../lib/gauche/interactive.scm... (use srfi-1) ;; Loading ../libsrc/srfi-1.scm... ;; Loading ../lib/gauche/common-macros.scm... ;; Loading ../lib/srfi-26.scm... (exit)
- isi(2007/09/12 07:36:02 PDT): 横から失礼します。これcygwinのシンボリック・リンクじゃないよね。Windowsのショートカットだ。なんで?
- kana(2007/09/12 08:49:00 PDT): はじめまして。CygwinのシンボリックリンクはWindowsのショートカットを利用して実装されているからだったと思います。Cygwinのln -sで作成したものについては特殊な属性が付いているので通常のショートカットとは区別されるはずです。でもlsして.lnk(ショートカットの拡張子)が見えるのはおかしいですね。
- Shiro(2007/09/12 12:44:53 PDT): catでlib/srfi-1.scmが見られないという ことはgaucheからも読めないということです。シンボリックリンクがちゃんとできてない ってことですね。このリンクはgoshスクリプトで作ってるので、src以下で./gosh -ftest を起動して (sys-symlink "core.c" "foo") などとしてシンボリックリンクfooが ちゃんと生成されるかどうかを確認してみてください。
- j-kon(2007/09/12 15:11:41 PDT):
>cd src >echo test > foo >./gosh -ftest (sys-symlink "foo" "bar") (exit) >ls bar* bar >cat bar test >ln -s foo foo.symlink >ls foo* foo foo.symlink >cat foo.symlink test
あれ?
- Shiro(2007/09/12 20:33:54 PDT): つまりgoshによるsymlink作成機能自体は動作していると。 妙ですねえ。上の「dllのパーミッション云々」というのも私は見たことが無いので、 ビルド途中で何か妙なことが起きているんだと思いますが… ちなみにうちの環境もXP SP2でgccのバージョンも同じです。 binutilsのバージョンはいくつですか? (cygcheck -c binutilsで見られます。) うちは20060817-1でした。
- j-kon(2007/09/12 22:18:06 PDT): お騒がせしましたが、以下の泥臭い作業をした所、ビルドできました。Windows XP の設定が何か変なのでしょう…
- *.scm と *.dll のパーミッションを手で直しつつ
- シンボリックリンクも *.lnk となっているものは手で直す
- j-kon(2007/09/12 22:18:06 PDT): binutilsのバージョンは
>cygcheck -c binutils Cygwin Package Information Package Version Status binutils 20060817-1 OK
という訳で一緒です。