• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:schmeですが違いがわかりません)

Schemeプログラムにおけるカウンターの違いとは?

このQ&Aのポイント
  • Schemeのプログラムで、make-counter1とmake-counter2という2つの関数が定義されています。
  • make-counter1はlet式を使用してカウンターを定義し、make-counter2はlambda式を使用してカウンターを定義します。
  • c1とc2はmake-counter1を呼び出した結果を代入し、d1とd2はmake-counter2を呼び出した結果を代入します。

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

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

>1はlambda式中の(lambda () (set! c (+ c 1)))を評価してますが、2は直接(set! c (+ c 1))を評価していますね。 まさしく「それが理由」なんですが……。要するに1番目のケースではラムダ式で(lambda () (set! c (+ c 1)) c)が「保護されている」のが主な理由なんですけど……。 「クロージャ」って単語聞いたことありますか?「計算機プログラムの構造と解釈」ではあまり説明されてないんですよね(笑)。あるいは「閉包」と言うちょっと「アレ?」って思うような単語が使われていて……。 これは概念的にはWikipedia辺りの解説読んだ方が良いかもしれません。 クロージャ - Wikipedia: http://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%A3 つまり、make-counter1~の状況は上のWikipediaの記述として言えば次の部分です。 クロージャはある関数全体が他の関数(以下、エンクロージャ)の内部で宣言されたときに発生し、内部の関数はエンクロージャのローカル変数(レキシカル変数)を参照する。 言い換えると次の感じですよね、 (lambda () (set! c (+ c 1)) c)がラムダ式(以下、エンクロージャ)の内部で宣言されたときに発生し、(lambda () (set! c (+ c 1)) c)はエンクロージャのローカル変数(レキシカル変数)を参照する。 つまり、ここのcはクロージャ外のlet式で作成されたcは「参照してない」のです。そしてそうである以上、クロージャ内部で作られたcは「保存されて」いて、常にそのcを参照しています。(クロージャ外部のletで作られたc≠エンクロージャのc)

noname#182748
質問者

お礼

大変丁寧な回答ありがとうございました。 本当に参考になりました。あつくお礼を申し上げます。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (2)

回答No.2

ラムダ式がゴチャゴチャしてると分かりづらいのは事実です。 では、逆になるたけ「ラムダ式」を使わなかったら? 次のコードを見比べて下さい、make-counter1~はmake-counter1に、make-counter2~はmake-counter2に、それぞれ対応しています。 (define make-counter1~ (let ((c 0)) (lambda () (lambda () (set! c (+ c 1)) c)))) (define (make-counter2~) (let ((c 0)) (lambda () (set! c (+ c 1)) c))) (define c1~ (make-counter1~)) (define c2~ (make-counter1~)) (define d1~ (make-counter2~)) (define d2~ (make-counter2~))

noname#182748
質問者

お礼

かいたおうありがとうございます。しかしやはりわかりません。 1はlambda式中の(lambda () (set! c (+ c 1)))を評価してますが、2は直接(set! c (+ c 1))を評価していますね。 2が評価するごとにことなるフレームをつくるのは理解できますが、なぜ1は異なる呼び出しでも同じフレームを呼び続けるのかがわかりません。

全文を見る
すると、全ての回答が全文表示されます。
回答No.1

make-counter2はmake-counter3に書き換えられます。 以下はそのコード。 (define make-counter3 (lambda () ((lambda (c) (lambda () (set! c (+ c 1)) c)) 0))) (define e1 (make-counter3)) (define e2 (make-counter3)) make-counter1と見比べて下さい。

noname#182748
質問者

お礼

回答ありがとうございます。両者ほとんどコードは同じですがcの位置はcounter1を前者とし、counter3を後者とすると前者は最初のlambda中で、後者は二番目のlambda中ですね。これがどうして違いを生むのかさっぱりわかりません。なぜでしょう…?

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • C言語 クロージャマクロの読み方

    #include <stdio.h> #define APPLY(type, closure, arg) (type)(&(closure), arg) #define APPLY_COUNTER(c, a) APPLY (((int (*)(Counter *, int))((c).func)), c, a) // <= #define MAKE_COUNTER(k) {k, counter_function}; typedef struct{ int n; void* func; }Counter; int counter_function (Counter * c, int a){ return c->n += a; } int main (void){ Counter c1 = MAKE_COUNTER (0); Counter c2 = MAKE_COUNTER (5); printf ("%d\n", APPLY_COUNTER (c1, 3)); /* => 3 */ printf ("%d\n", APPLY_COUNTER (c2, 1)); /* => 6 */ printf ("%d\n", APPLY_COUNTER (c1, 2)); /* => 5 */ printf ("%d\n", APPLY_COUNTER (c1, 8)); /* => 13 */ printf ("%d\n", APPLY_COUNTER (c2, 9)); /* => 15 */ } #define APPLY_COUNTER ここの APPLY への値渡しが読み解けません 詳しい方、教えてください

  • [Scheme] リストへの破壊的代入について

    以下のプログラムが動かない理由がわかりません. (define cdrlst  (lambda (lst)   (set! lst (cdr lst)))) 内容としては,引数で与えられたリストの先頭を取り除くというものです. 対話的な方法で以下のように実行すると,上記と同様の方法でも動作しますが,ラムダ式の中に入れると動作しなくなります. > (define cdrlst ... (略)) > (define l (list 1 2 3 4)) > (set! l (cdr l)) > l (2 3 4) > (define lst (list 1 2 3 4 5) > (cdrlst lst) > lst (1 2 3 4 5) どうか,よろしくお願いします. 処理系 : DrScheme version 370 [3m] ;Standard(R5RS)

  • 1を加える足し算と、インクリメントの違い

    数値Nを1つインクリメントすることと、数値Nに1を足すことには数学的な意味の違いがありますか? 電子回路で言うなら前者は数値Nを設定したアップカウンタにクロック一つ分のカウント許可を与える操作、後者は加算器の入力にNと1を与える操作を想定しています。もちろん結果は同じで論理的には同じ操作ということになると思います。 何となくですが、前者は「数える」ことに近い感じで数そのものの定義に密接なイメージ、後者は加えるものが1で無くても同様に計算できるのでもう少し高級な意味ではないかというイメージを持っています。数えることはできるが足すことは難しい、という知的能力の段階があるような気がしています。興味の中心は数学的意味の違いの有無です。

  • -1 はいくつ

    下記 C プログラムを実行しました。 printf ("char:%x\n",(char)-1); printf ("unsigned char:%x\n",(unsigned char)-1); 前者では -1 が ffff、後者では -1 が ff と出力されました。どうしてこうなるのでしょう。私の C コンパイラのムシでしょうか。

  • Cの変数の初期化のタイミングについて

    以下のようなCの演習プログラムについてお尋ねします。 やっていることは、inc_counterと言う関数を呼び出して呼び出すたびに変数を1つづつ増やすというもので、10回呼び出すので変数が10になるというものです。この演習プログラムが言いたいことは変数counterの動作ということです。 externですべての関数の外側で宣言されているから呼び出す側、呼び出される側で共通になっているということですが、void inc_couter(void)の実体(後方)の前にint counter=0;となっています。この文は変数counterの初期化ということだと思いますが、これが実行されるのはいつなのでしょうか。関数inc_counerの呼び出しのときは実行されないのでしょうか。もしそうなら関数が呼び出されるたびに初期化されるはずですが、そうではないのでしょうか。 変数のスコープとか初期化とかの問題だと思いますが、この辺を間違って理解すると大けがをしそうなのでお尋ねします。よろしくお願いします。 #include<stdio.h> extern int counter; void inc_counter(void); main() { int index; for(index=0; index<10; index++) inc_counter(); (void) printf("Counter is %d\n",counter); return (0); } int counter=0; void inc_counter(void) { counter++; }

  • エクセルマクロ シート名称と他のファイル間の操作について

    シート名称に、「NY(1)」というように、()を使っています。 多分この()が原因だと思われますが、 Dim sh1 As worksheet Set sh1 = worksheets("NY(1)") と定義しても、うまく作動しません。 「実行時エラー”9” インデックスが有効範囲にありません。」 というメッセージが出てしまいます。 私の要望は、sheets("データ")上のコマンドボタンから、sheets("NY(1)")にあるプログラム(例えばコピーなど)を実行したいものです。 こういった場合、どのような定義?構文を組めば作動するでしょうか? 次に、このブック(ブック名=NY100)から(ブック名=NY200)のデータを操作する場合、(前者のブックにあるデータを、後者のブックに複写して、前者のコマンドボタンを押すことによって、後者のプログラム(例えばコピーなど)を実行させる)には、どのような定義?構文を組めば作動するでしょうか? どなたか教えてください。 ちなみに、エクセル2003、OS=XPです。

  • makeの勉強中なのですが・・・

    新米プログラマです。 今、makeの勉強中なのですが makeの学習本中で使われている言葉にいくつかわからないことがあります。 例えば、 サフィックスで .c : $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ という定義がされ、 $make program というターゲットを実行するとします すると cc -O program.c -o program というコマンドが実行されると書いてあります。 また、 "-cオプションが定義されていないので、ldも自動で実行される" と書いてあるのですが、ldとは何のことなのでしょうか? よろしければ、サフィックスの解説もお願いします。

  • 下記別シートがあり、比較して異なる行を判別する方法を教えてください。

    下記別シートがあり、比較して異なる行を判別する方法を教えてください。 (下記例では後者シートにおいて、3行目と4行目が該当します) ・判別結果表示は、後者シート上に該当する行に地色を付けるなど、フィルタで該当行をまとめられる様にできれば目印は何でも結構です。 ・後者シートは前者と比較して行(レコード)が減ったり、値が無くなることはありません。 前者に足された結果が後者になる A列 B列 C列 D列 1 a A 2 b 4 d D 5 e E A列 B列 C列 D列 1 a A 2 b 3 c C 4 d D F 5 e E

  • #ifdef _DEBUGが効かない

    こんにちは。 Win2000、VC++6.0使用。 今までC言語でプログラムを組んではいましたが、 #define _DEBUG #ifdef _DEBUG print(...); #endif というようにデバッグ用のコードを入れたことはありませんでした。 このような便利なものがあると知り、早速使ってみたのですが、 #define _DEBUG の一文をコメントアウトして、再コンパイル(リビルド) して実行してみたのですが、 普通に #ifdef _DEBUG #endif で囲まれた部分が実行されてしまいます。 #define _DEBUG を宣言しなければ、実行されないと思っていたのですが 自分の勘違いでしょうか? よくわかりません。 どなたかご存知の方、よろしくお願いします。

  • C言語の課題で困っています。その1.四則演算

    二つの整数値を読み込んで、前者を後者で割り、その結果の商と余りを表示するプログラムを作成。 実行例にあるよう、ゼロで割るような場合への対処も考慮する。 実行例1: 整数の除算を行います。整数を入力してください。 整数A:13 整数B:5 13 ÷ 5 = 2 あまり 3 実行例2: 整数の除算を行います。整数を入力してください。 整数A:10 整数B:0 0でわることはできません! (注)プログラム実行時にキーボードからどんな整数を入力してもいいように作成すること。 という課題に取り組んでいますが、途中までしか記述できません。 どなたか助けてください。 下記が途中までの記述です。 /*二つの整数値を読み込んで、前者を後者で割り、その結果の商と余りを表示*/ #include <stdio.h> int main(void) { int na, nb; printf("shimasaki kazunori \n"); puts("整数の除算を行います。整数を入力してください。: \n"); printf("整数A:"); scanf("%d", &na); printf("整数B:"); scanf("%d", &nb); printf("na ÷ nb = %d あまり %d \n", na / nb, nb, na % nb); return(0); }