遅延評価がうまくいかない問題の解決方法

このQ&Aのポイント
  • 遅延評価がうまくいかない問題が発生し、integersの定義時にプログラムが落ちる
  • integers-starting-from関数を修正して遅延評価が正しく行えるようにする
  • プログラムの実行速度を向上させるために、素数の生成処理を最適化する
回答を見る
  • ベストアンサー

落ちてしまいます

無限ストリームなのですが、 (define (stream-car stream) (car stream)) (define (stream-cdr stream) (force (cdr stream))) (define (cons-stream a b) (cons a (delay b))) (define (integers-starting-from n) (cons-stream n (integers-starting-from (+ n 1)))) (define integers (integers-starting-from 1))      (define (stream-ref s n) (if (= n 0) (stream-car s) (stream-ref (stream-cdr s) (- n 1)))) (define (divisible? x y) (= (remainder x y) 0)) (define (sieve stream) (cons-stream (stream-car stream) (sieve (stream-filter (lambda (x) (not (divisible? x (stream-car stream)))) (stream-cdr stream))))) (define primes (sieve (integers-starting-from 2))) (stream-ref primes 10) integersを定義する段階で落ちてしまうようです。どうも遅延評価がうまくいってないようです。どうしたらよいでしょうか?どなたか助けてください。

noname#182748
noname#182748

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

  • ベストアンサー
回答No.1

「計算機プログラムの構造と解釈」でしょ? 前も誰か投稿してたと思うんですけど、これって「激ムズの」教科書なんで、ここで訊かない方が良いですよ(笑)。SICPで検索すれば「勉強会」やってる専門サイトがいくつか見つかると思うんで、そっちで尋ねた方が良いです。 少なくとも、Schemeやこの教科書使ってる人ってのは「偏差値高めの」大学でキチンとした「計算機工学」を学んだ/学んでる人たちが多いんで、質問投稿するにせよ、場所を絞った方が良い、と思います。 しかも、投稿のコード見ると、まずは、 stream-filter と言う手続きが「未定義」なんですよね。従って、仮にScheme持っててもこれじゃあ走りません。 「計算機プログラムの構造と解釈」見る限り、次の手続きと変数を定義しとかなきゃならないのです。 (define (stream-filter pred stream) (cond ((stream-null? stream) the-empty-stream) ((pred (stream-car stream)) (cons-stream (stream-car stream) (stream-filter pred (stream-cdr stream)))) (else (stream-filter pred (stream-cdr stream))))) (define (stream-null? string) (null? string)) (define the-empty-stream '()) でしょ?これ抜けてる以上、上の質問は不完全です。これも「教科書」共有してないと指摘出来ません。 さて、投稿コードを見ると、次のコードがおかしいのです。 (define (cons-stream a b) (cons a (delay b))) これが問題です。と言うかこれが原因ですね。 「計算機プログラムの構造と解釈」見てみれば分かりますが、これってご自分で書いたんでしょ?と言うか、当該の教科書には「ハッキリとした」定義書いてないんですよね(笑)。 p.189辺りの下に次のような事が書かれています。 stream-carとstream-cdrは手続きとして定義出来るが、cons-streamは「特殊形式でなければならない」。 上のnerudaさん自作のコードは「手続き」です。従って、この条件を満たしていません。 ついでに、この議題は実はもっと先のp.243以降に持ち越されます。従って、「無限ストリーム」と言うお題ではここのコードは「動かせない」のです。 現時点での回避法は、平たく言っちゃうと、「特殊形式」を簡単に定義する……要するに「マクロ」としてcons-streamを設計しちゃう、と言う方法があります。 しかしながら、これはある意味「掟やぶり」で、と言うのも、「計算機プログラムの構造と解釈」ってマトモなマクロ解説、って無いんですよ。 現状のScheme、少なくともR5RSやR6RSは言語仕様としてマクロ持っていますが、「計算機プログラムの構造と解釈」が上梓された当時は、多分R3RSかR4RSの頃で、少なくとも「Appendix」扱いでマクロは正式仕様じゃなかった、んです。ちょっと「マクロの実装方法」に対して、Schemeコミュニティがまだ神経質だった頃に出版された本なんです(だからこそ、「特殊形式」でのstream-consの明確な記述を避けたのでしょう)。 実は今も「Schemeのマクロ実装方法」ってちょっと揉めてるんですけどね(笑)。一応、R5RSの正式なマクロ(なんだけど、これで書く人が少ない・笑)である「パターンマッチング式」のマクロでのstream-consの記述は以下の通り、です。 (define-syntax cons-stream (syntax-rules () ((_ a b) (cons a (delay b))))) これだと引数a、bが「評価される事を」止めます。平たく言うと、マクロcons-stream自身は評価されず、呼び出し元のコード内で、(cons a (delay b))と言う形で「展開される」ようになるんです。 これを組み込んで動作させれば望む結果が得られるでしょう。例えば > (stream-ref primes 10) 31 のように。

noname#182748
質問者

お礼

回答ありがとうございます。おっしゃるとおりSICPです。 stream-filterはコピペしそこなっていました。もうしわけございません。現在研究室ができたばかりなので学生は僕一人で先生と毎週30P進んでいます、誰にもきけず孤軍奮闘で、なかなかきついです。 stream-carとstream-cdrは手続きとして定義出来るが、cons-streamは「特殊形式でなければならない」。 そうですね、自分でもおかしいと思いながらなんとか書いてみたとこです。 回答者様のおっしゃるようにしたらきちんと動きました。本当にありがとうございました。

関連する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が有効でなくなりエラーになると考えたのですが、実際は動きました。 どこがおかしいのでしょうか。環境モデルでも考えてみたのですが分かりませんでした。よろしくお願いします。

  • Shemeで掛け算の関数

    Shemeで掛け算の関数 関数muliを定義しなさい。ただしzero,inc,dec,add,mulはすでに定義されて、 muliはmulの中から(mulli x y (zero))と呼ばれる。 なんとなくわかるかもしれませんが、 (define (dec n) (cdr n)) (define (zero) (quote())) (define (add x y) (if (null? y) x (add (inc x) (dec y)))) となっています。 この問題で (define muli (lambda (x y n) ((if (null? y) n (muli x (dec y) (add n x)))))) と考えたのですがうまくいきません。 ご教授よろしくお願いします。

  • 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)))))))

  • 【C→JAVA】素数の組の数を求めるプログラム

    以下はC言語のプログラムです。 標準入力に正の偶数値 n(2≦n≦10,000) を入力すると足して n になる素数の組の数を求め、 標準出力に出力するプログラムなのですが、 これをJAVA用のプログラムに置き換えるとすると どのようなプログラムになるのでしょうか? よろしくお願いいたします。 #include <stdio.h> #include <math.h> //Compiler version gcc 6.3.0 #define N 10000 int primes[N + 1] = {0}; void sieve(int); int main() { int n,count = 0; sieve(N); scanf("%d",&n); for (int i = 1;i <= n / 2 + 1;i++) { for (int j = i + 1;j < n;j++) { if (primes[i] && primes[j] && i + j == n) { count++; } } } printf("%d\n",count); return 0; } void sieve(int n) { int limit = (int)sqrt(n) + 1; for (int i = 2;i <= n;i++) { primes[i] = 1; } for (int i = 2;i < limit;i++) { if (primes[i]) { for (int j = 2; i * j <= n;j++) { primes[i * j] = 0; } } } }

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

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

  • Lispについてわからないことが(Scheme)

    あるLispの勉強ソフトで、 「関数lを『引数としてxを受け取ると、xの要素の数を返す関数』として定義しなさい。」 という問題があるのですが、私は以下のようにしました。 (define l (lambda (x) (if (= x null?) 0 (+ 1 (l (cdr x)))))) しかしこれだとオーバーフローと表示されて強制終了されてしまいました。 そこで答えをネットで検索したところ以下のものが見つかりました。 (define l (lambda (x) (cond ((null? x) 0) (else (+ 1 (l (cdr x))))))) これが正解なようですが、この2つのリストの違いがわかりません。 初歩的なことですがifの使い方を間違っているんでしょうか?

  • Shemeで

    Shemeで 関数map1を引数を1つとる関数と リストの2つの引数をとる、簡易版のmapとして定義しなさいという問題で 以下のように考えたのですがうまくいきません。 (define map1(lambda (f l) (if (null? l) (quote()) cons(f (car l) (map1 f (cdr l)))))) ご教授お願いします。

  • emacsでcmmon lispのプログラムを作成します。

    emacsでcmmon lispのプログラムを作成します。 my-equal(A, B)= if A is an atom then if B is an atom then (eq A B) else if B is an atom then nil else if (car A)=(car B) then (cdr A)=(cdr B) else nil というのです。自分が考えたのは、 defun my-equal(x, y)= (cond ((atom x) (atom y)) (eq x, y) (atom) (t, nil) ((car x)=(car y) (cdr x)=(cdr y)) (nil)) というのでよろしいのでしょうか? また、4行目(この全文の)の意味がいまいち分かりません。

  • scheme

    リストに含まれる記号の数を求める手続きを作りたいのですが。 (0 -1 -2 -3) この場合は0を返す (pooh piglet 2001)  この場合は2を返す (1192 538 (owl rabbit))  この場合は2を返す 再帰を使うのですがうまくいきません。 1つめと2つめの例はできるのですが、3つめは中にリストがあって そのリストの中も数えるところがよくわかりません。 (define (count x) (if (null? x) 0 (if (number? (car x)) (count (cdr x)) (+ (count (cdr x) 1))))) これだとうまくいきませんでした。 おねがいします。

  • YUV動画像を1ピクセルごとにずらしていく方法がわかりません・・・

    動画像を1フレーム毎に1ピクセルずつ右にずらし右にはみでたピクセルは左の欠けた部分にそのまま補完するプログラムを作りたいのですが途中からわかりません;; ピクセルどどうやって右のやつを左にもっていくのかがさっぱりで・・・どなたかお願いします。 #include <stdio.h> #include <stdlib.h> #define x_size 352 #define y_size 288 #define FN 200 typedef struct { unsigned char Y[x_size*y_size]; unsigned char U[x_size*y_size/4]; unsigned char V[x_size*y_size/4]; } FRAME; int main(void) { FILE *fpr; FILE *fpw; char file_name1[100]="zikken2.yuv"; char file_name2[100]="./out.yuv"; int i, frame_num,j; FRAME *ref; if((fpr = fopen(file_name1, "rb")) == NULL){ fprintf(stderr, "Can't open %s in read mode\n", file_name1); exit(-1); } if((fpw = fopen(file_name2, "wb")) == NULL){ fprintf(stderr, "Can't open %s in write mode\n", file_name2); exit(-1); } ref = (FRAME *)malloc(sizeof(FRAME)); /* read from file */ for(frame_num=0; frame_num<FN; frame_num++){ for(i = 0; i<x_size*y_size; i++){ ref->Y[i] = getc(fpr); } for(i = 0; i<x_size*y_size/4; i++){ ref->U[i] = getc(fpr); } for(i = 0; i<x_size*y_size/4; i++){ ref->V[i] = getc(fpr); } /* write to file */ fwrite(ref,1,sizeof(FRAME),fpw); } fclose(fpr); fclose(fpw); free(ref); return 0; }

専門家に質問してみよう