• ベストアンサー

scheme gaucheに関して

gaucheで次のプログラムを書きました. ; (define (func lists)  (cond ((null? lists) (cons '() '()))     (else (cons lists (func (cdr lists)))))) このプログラムでは次のような結果を期待していました. gosh> (func '(1 2)) ((1 2) (2) ()) しかし実際には次のような結果になりました. gosh> (func '(1 2)) ((1 . #0=(2)) #0# ()) 他の処理系(Dr.Schemeで行った)で行ったところ, 期待していた通りに実行できたのですが, goucheでは((1 . #0=(2)) #0# ())という結果になってしまいました. この表示の意味するところは何なのでしょうか. #0などが出てきていますがどのような意味なのでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

う~ん, なんかうまく添付できない.... そういうことです. もっと端的にいうと (define a '(1 2)) (set! (car a) a) でも同じようになります (コンスセルの car 部が自分自身を参照しているため, a を表示させると #0=(#0# 2) と表示されます). もちろん, 共有するコンスセルが複数ある場合には #0, #1, ... と異なるラベルで表示されます.

pikacha
質問者

お礼

先ほどの例で '(1 2 3) にfuncを適用してみたところ gosh> (func '(1 2 3)) ((1 . #0=(2 . #1=(3))) #0# #1# ()) となりました。 今説明していただいたようにこの場合は#0と#1を使って共有するコンスセルを表しているのですね!今度は共有するところが二カ所あるので二つのラベルを使って表しているわけですね。 詳しい説明をありがとうございます。

その他の回答 (3)

回答No.3

ああ、そうですね。Gauche独特(ってわけでも無いんでしょうけど:実際はSRFI38定義)な表記法ですよね。 これは循環リストとかを表現するのに便利だったりするんです。 R6RSでは重要度が下がった手続きで、もはやPLT Scheme(DrScheme)では禁止された手続きに、リストのcdr部を破壊的に書き換えるset-cdr!と言う手続きがあります。 例えばリストがこう言う構造を持ってるとします。 35→24→42→19→(先頭の35を指す) これをGaucheでパーツ分けして作ってみます。 gosh> (define lst0 '(35 24)) lst0 gosh> (define lst1 '(24 42)) lst1 gosh> (define lst2 '(42 19)) lst2 gosh> (define lst3 '(19 35)) lst3 今、lst0~lst3までの4つのパーツがありますね。 まず、lst0のcdr部をlst1で書き換えてみます。 gosh> (set-cdr! lst0 lst1) #<undef> gosh> 「破壊的にlst0のcdr部をlst1を用いて書き換えた」わけですから、当然lst0は変更されています。 gosh> lst0 (35 24 42) 今度は同様にlst1のcdr部をlst2を用いて書き換えます。 gosh> (set-cdr! lst1 lst2) #<undef> gosh> これは破壊的操作(通常の手続き型言語で言うと代入操作:あるいはポインタが指し示しているアドレス変更)なんで、lst1の書き換えは当然lst0に影響します。 gosh> lst0 (35 24 42 19) gosh> 今度はlst2のcdr部をlst3で書き換えます。lst0の変化にまたもや注目してください。 gosh> (set-cdr! lst2 lst3) #<undef> gosh> lst0 (35 24 42 19 35) gosh> 今、上のlst0のcarである35と最後の35は全く違うものです。今度は「lst3のcdr部をlst0で」書き換えてみます。 gosh> (set-cdr! lst3 lst0) #<undef> gosh> さて、lst0はどうなるのか? gosh> lst0 #0=(35 24 42 19 . #0#) gosh> 循環リストの出来上がり、です。実際は35 24 42 19 35 24 42……とまさしく「循環」していて、これは表記し辛い(だけじゃなくってちょっと危険・笑?)んですが、Gaucheでは上のようなシンプルな表記で循環構造を示す事が可能となっています。 参考ページをあげておきます。 Gauche:循環リストの読み書き: http://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3A%E5%BE%AA%E7%92%B0%E3%83%AA%E3%82%B9%E3%83%88%E3%81%AE%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D この表記が気にくわない場合は、Gaucheでは (write リスト) で一時的に解除出来ます。 例えば、題意のコードの場合は、 gosh> (define (func lists)     (cond ((null? lists) (cons '() '()))         (else (cons lists (func (cdr lists)))))) func gosh> (write (func '(1 2))) ((1 2) (2) ())#<undef> gosh> と見た目解除が可能です。 ただし、このwrite手続きは、循環リスト相手に使うと表示の無限ループに陥っちゃうので気を付けてください(文字通り「本当に」循環してるので)。

pikacha
質問者

お礼

ご回答ありがとうございます。 循環リストのときには参照を使わずに表示すれば無限ループになってしまうわけですね。循環リストの表示では参照の表示を使わなければ有限回で表示できなくなってしまいますね。この場合は参照の表記を使うことで、シンプルに表示できるのでとても効果的な表示法なのですね! wirte を教えていただきありがとうございます。今回の問題ではリストの部分リストを取り出したかったので、参照の記法よりもwrite を使った表示の方が好ましかったので教えていただけて大変助かりました。 wirte は無限ループに陥ることもあるので、注意深く使っていきたいと思います。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

画像の添付がなんかうまくいかないので改めて: ((1 2) (2) ()) と ((1 . #0=(2)) #0# ()) は「表面的」には同じです. ただし ・((1 2) (2) ()) では, 1つ目の (1 2) の (2) の部分と 2個目の (2) は違うコンスセルになる ・((1 . #0=(2)) #0# ()) では 1つ目の (1 2) の (2) と 2個目の (2) でコンスセルを共有する という違いがあります. #0= でコンスセルに対してラベルを張り, そのあと #0# でそのラベルを参照しています. この辺はリストをコンスセルで描いてもらうとわかりやすいんだけど.... 今度は添付できるかな?

pikacha
質問者

お礼

回答ありがとうございます。 なるほど!!この#0というのはどのコンスセルが共有されているのか ということを表すためのラベルだったのですね。 はじめは何が表記されているかわからずに驚きましたが、仕組みがわかるとコンスセルが共有される仕組みがわかって勉強になります。 画像添付の件でも何度も回答していただき、ありがとうございました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

? と思ったけど, 手元で実験してわかった. この結果は, 「表面的」には ((1 2) (2) ()) と同じです. ただし, ((1 2) (2) ()) と ((1 . #0=(2)) #0# ()) では, コンスセルのレベルでは違う結果になります. 後者では, 最初のリストにある (2) の部分と 2個目の要素である (2) が共用されています. この共用関係を #0 と #0# で表しています. と書くより絵の方がきっと説明しやすい.

関連するQ&A