• ベストアンサー

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

  • scheme でのスコープについて

    次のようなcar,consの定義をします. ;;consの定義 (define (cons x y)  (define (dispatch m)   (cond ((= m 0) x)      ((= m 1) y)      (else (error "Argument not 0 or 1 -- CONS" m)))) ;;carの定義 (define (car z) (z 0)) ここで、 (car (cons 1 2)) を評価したいのですが、次のような置き換えモデルで考えてみました。 (car (cons 1 2)) (car dispatch) ;まず引数を評価 (dispatch 0) ;car を評価して引数に作用させる 1 質問は、dispatchの有効範囲についてです。 dispatchはconsの内部定義なので(cons 1 2)を評価後、carに引数として渡すには,その時点でdispatchが有効でなくなりエラーになると考えたのですが、実際は動きました。 どこがおかしいのでしょうか。環境モデルでも考えてみたのですが分かりませんでした。よろしくお願いします。

  • Schemeについての問題

    今大学でSchemeについて学んでいるのですが、そこで出た問題がわからなくて困っています。 よければ、教えていただけないでしょうか。 問題は以下の通りです。 問1 次に示すScheme プログラムについて以下の問に答えよ。 (define (subtree? t1 t2) (cond ((atom? t1) (eq? t1 t2)) (#t (cond ((atom? t2) #f) (#t (or (and (subtree? (car t1) (car t2)) (subtree? (cdr t1) (cdr t2))) (or (subtree? t1 (car t2)) (subtree? t1 (cdr t2))))))))) 関数subtree?は二つのS 式(S 表現) t1, t2 を入力とし、真偽値(#t あるいは #f) を返す関数である。 関数subtree?が真(#t) を返すための必要十分条件は何であるか答えよ。 また、関数subtree?が実際そのような関数であることをS 式に関する帰納法を用いた議論によって示せ。 考えたのですが、わからなくて。こんな質問してしまって申し訳ないです。 よければよろしくお願いしますm(_ _)m

  • Schemeの質問

    最近Schemeを習い始めたのですがlambdaの概念がよく分かりません。 適当に見つけた例題やらを見ながら、末尾再帰法でリストの中から一番小さい数を表示する関数を書きました。リストが空なら'()を返します。 (define (small List) if (null? List a) '() (small2 List 0))) (define small2 (lambda (list a) (cond ((null? List) a) (or (= head 0) (> head (car List))) (small2 (cdr List) (car List))) (else (small2 (cdr List) a))))) 一応ちゃんと動くのですが、無駄にいっぱい書いている気がします。lambdaを使わない方が短くできるんでしょうか?lambdaを使うべきなのか使わないべきなのかの判断がうまくできません。

  • だれでもわかるそうです。

    ただいま仕事も引退して老後の生活を送っています。 老後の趣味でパソコンを勉強しようと思いschemeをしています。 買った問題集の回答がよくわからないのでここで質問させていただきたいと思います。 だれでもできる簡単問題らしいのでだれか答えてくださると幸いです。 (define (wrap l) (cond ((null? l) (cons l l)) (#t (cons ここになにかをいれろ)))) って問題です。任意のリストlを(l)で返すプログラミングだそうです。 どうかお願いします。ぺこり。 前 あとwを任意のs表現としてS表現のみを要素とする長さ1のリスト(w)を、w,car,cdr,consおよび()を用いて表現できなかったので、だれかヒントをください

  • Scheme 中置式から後置式へ

    こんばんは。こちらのカテゴリには初投稿になります。 ただいまScheme習いたて、はじめたばかりの大学一年生です。 二つ、どうしてもわからない問題があります。 問A 中置式リストを後置式リストに変換する関数を定義せよ 問B 後置式の数式リストmとリスト表現のスタックsを受け取り、sを用いてmを実行し、最終のsを返す関数stackMを定義せよ という二問です。 Aに関しては、 ;; revP : list -> list ;; (revP (list 2 * (list 4 - 1)) should return (list 2 4 1 - *) (define (revP l) (cond [(empty? l) empty] [else (cond [(number? (first l)) (cons (first l) (revP (rest l)))] [else ....] まで考えましたが、これでは当然のごとくまったく動きません。。 Bに関して、 ;; stackM : list, list -> list ;; (stackM (list 2 4 1 - *)) should return (list 6) (define (stackM m s) (cond [(empty? m) empty] [(number? (first m)) ] [else....])) と3つに分けるところまではヒントを見てわかったのですが、これから先どうすればいいのかわからないです。。 どなたかできればどうすればいいのかという方針をお教えいただけないでしょうか?かれこれ2時間以上考えていますがまったく出てきません。。 どうかよろしくお願いいたします。 最後に、長文の質問を最後までお読みくださりありがとうございました。

  • schemeのことです

    schemeのことです expression datatypeを作りました 一番最後のexp->string部分で困っています consとかifとか使うべきですか? -------------------------------- (define-datatype expression expression? (const-exp (num number?)) (var-exp (var symbol?)) (zero?-exp (exp1 expression?)) (diff-exp (exp1 expression?) (exp2 expression?))) ;; Predicates. (define const-exp? (lambda (exp) (cases expression exp (const-exp (num) #t) (else #f)))) (define var-exp? (lambda (exp) (cases expression exp (var-exp (var) #t) (else #f)))) (define zero?-exp? (lambda (exp) (cases expression exp (zero?-exp (exp1) #t) (else #f)))) (define diff-exp? (lambda (exp) (cases expression exp (diff-exp (exp1 exp2) #t) (else #f)))) ;; ====================================================================== ;; Extractors. (define exp->const (lambda (exp) (cases expression exp (const-exp (num) num) (else #f)))) (define exp->var (lambda (exp) (cases expression exp (var-exp (var) var) (else #f)))) (define exp->exp1 (lambda (exp) (cases expression exp (zero?-exp (exp1) exp1) (else #f)))) (define exp->exp2 (lambda (exp) (cases expression exp (diff-exp (exp1 exp2) exp2) ;; ====================================================================== ;; exp->string: exp -> string ;; Abstract syntax to concrete syntax. (define exp->string (lambda (exp) (cond ((const-exp? exp) (format "~a" (exp->const exp))) ((var-exp? exp) (format "~a" (exp->var exp))) ((zero?-exp? exp) (format "zero?(~a)" (exp->exp1 exp))) ((diff-exp? exp) (format "-(~a,~a)" (exp->exp2 exp)(exp->exp2 exp))) (else #f

  • schmeスキームのプログラミングについて質問です

    listから条件に合わない数を消すfilter1という関数をつくりたいです。 ただ条件があって次のseries関数を使わなければいけません。 (define (reduce op base L) (cond [(empty? L) base] [else (op (first L) (reduce op base(rest L)))])) 自分は以下のような補助関数を含むプログラムを作ったのですが、****の部分をどうすればいいかわかりませんでした。 (define(filter1 rel_op L t) (cond [(empty? L) empty] [(rel_op (first L) t) (reduce f1 0 L)])) (define (f1 L t) (cond [(empty? L) empty] [***** (cons (first L)(rest L))] [else (rest L)])) もしよろしければ回答お願いします。 根本的に間違っているのなら、どのような道筋でプログラムを作るかだけでもいいので教えてください。

  • LISPによる横型探索

    LISPのSchemeをつかって横型探索で探索した経路を出力するプログラムをつくっているのですが、 今は探索が終わると終了するとCLOSEDの中を表示するプログラムしかできていません もしこれに探索した経路の出力をする機能を加える場合どうすればいいですか? OPEN→待ちリスト CLOSED→展開済みリスト GOAL→目標地点 OP→オペレータの集まり nとopから展開の結果をだす (define (tenkai n op) (tenkai2 n op '())) (define (tenkai2 n op kekka) (if (null? op) kekka (if (= n (car (car op))) (tenkai2 n (cdr op) (cons (car (cdr (car op))) kekka)) (tenkai2 n (cdr op) kekka)))) (define (yokogata open goal op closed) (if (null? open) 'sippai (if (= (car open) goal) (display closed) (yokogata (append (cdr open) (tenkai (car open) op)) goal op (append closed (list (car open)))))))

  • 複数ファイルに分割した時の構成について

    大学の卒論にむけてプログラムを書いていて、一つのmain.cでは長くなってきたので、関数を func1.h func1.c のような別ファイルに記述することにしました。 その現在の構成を、質問欄の下部に簡単に書いたので、構成が自然かどうか意見をお聞きしたいです。特に、 「include文、define文をヘッダファイルにまとめて、各ファイルからincludeしている構造」がコードの記述方法として、きもちわるくないか、一般的かどうか、教えてください。 このようにした理由は、 ・defineマクロを多くのサブ関数で利用している。 ・define定義の値を細かく変えながら実行し、結果の違いを出力したい。 ・その為、各ファイルの頭にdefine定義を置いた場合、定義の値を  いちいち全てのファイルで変更しなければならなくなり、面倒…。 →defineマクロをdefine.hにまとめた。ついでに、<stdio.h>,<math.h>なども置いてしまえばスッキリするかも~。 という経緯です。 理学の学科でまわりに詳しい人がいなく、 プログラム知識もほぼ独学なので、不安に感じて質問しました。 よろしくおねがいします。 +++++++++++++ 以下が、簡単なファイル構成です +++++++++++++ -----define.h----- #include <stdio.h> #include <math.h> #define A_MAX 3 #define B_MAX 5 -----main.c---- #include "define.h" int main() { func1(); func2(); func3(); } -----func1.h---- #include "define.h" void func1(int hoge); //プロトタイプ宣言 -----func1.c---- #include "func1.h" void func1() { hogehoge; } ------func2以下、func1と同構造

  • LISPでatomの数を数える

    XLISPでlistの中のatomの数を数えたいんです。 下のようにlistの中のatomだけを抜き出してリストにすることはできました。 (DEFUN F1(L) (COND((NULL L) NIL) ((LISTP(CAR L))(F1(CDR L))) (T (CONS (CAR L)(F1(CDR L)))) ) ) このコードを実行すると次のようになります。 (F1 '((A B) C D (E F) G)) (C D G) 後はこれをlengthで数えるだけだと思うのですがそのやり方が分かりません。 それとももしかしてSETQで変数を設定して Tのところで値を1つずつ足していくのでしょうか?

専門家に質問してみよう