schemeでのスコープについて

このQ&Aのポイント
  • schemeでのスコープについて説明します。
  • schemeのconsとcarの定義に関して説明します。
  • dispatchの有効範囲について疑問があります。
回答を見る
  • ベストアンサー

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

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

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

この辺、「関数を返す関数を書ける」ってのがちょっと混乱するんですよね(笑)。Lisp系言語ですと割に平気でそれやっちゃうんで。 ポール・グレアムの「ANSI Common Lisp」でもいきなり累積器(アキュムレータ)の例を出して、「どうだ?こんなの他の言語じゃ書けないだろ?」的な例として挙げてたりします。 (defun addn (n)   #'(lambda (x)     (+ n x))) この辺の「クロージャを返せる」ってのは意図しないとあまりやらないんで、コードを読む際、「あれれれれれ?」ってなる確率が高いんで、気を付けた方が良いですね。 特に、SICPは「アセンブリ言語的な事を」Schemeでやってみせる、ってノリなんで、かなりこの辺は難しいですよね。件のcons、car、cdrの例も、Schemeの手続きとしての説明、と言うよりもコンピュータ上のデータ構造の解説、って感じですし。 (実際、データと手続きの区別が無い、ってのはアセンブリ言語の特徴、だそうです。) ちなみに、クロージャを返す、って事を徹底的に利用したサンプルに次のブログで紹介されているコードがあります。これ見た時僕はひっくり返りました(笑)。ネタはクダラない(って言っちゃえばアレなんですが・笑)んですが、プログラムの命令を人間が読める形式にクロージャを駆使して持ち込んでる、ってのが凄いな、と思いました。 http://d.hatena.ne.jp/yshigeru/20080418/1208523696

pikacha
質問者

お礼

schemeを始めてまだ間もないもので、関数を返す関数や、関数を引数にとる関数というのに少し混乱してしまいます。 ブログの紹介ありがとうございます。 schemeは本当に何でもできてしまうんですね! 引数や戻り値に関数をとることができることはとても抽象度の高いことなんですね! これから慣れていけたらと思います。 細かなことまで教えていただきありがとうございました。

その他の回答 (1)

回答No.1

「計算機プログラムの構造と解釈」でしょ? 最初のコードって一行足りないんじゃないですか? (define (cons x y)  (define (dispatch m)   (cond ((= m 0) x)      ((= m 1) y)      (else (error "Argument not 0 or 1 -- CONS" m))))    dispatch) ;; ここが足りない 多分この辺のかなり難易度の高い教科書に関しての質問はここにポストするより、例えば……そうだなあ、 SICP Reading Wiki: http://www.csus4.net/hiki/SICPReading/ 辺りにでもポストした方が有用だとは思うんですけどね。難しすぎて(笑)。 有効範囲は仰るとおり、だと思いますよ。 ただし、SICPのconsの例だとクロージャを返してるから、でしょうね。だから「環境を閉じ込めて」そのまま返している。 SICPに次の記述がありますね。 >気をつけるべきは(cons x y)の返す値が「手続き」(※ここが重要)であることだ。つまり内部的に定義された手続きdispatchで、これは一つの引数をとり、引数が0か1かによってxかyを返す。これに対応して(car z)はzを0に作用させると定義している。 つまり、上の例だと(car (cons 1 2))ってのは ((cons 1 2) 0) ってのが内訳でしょうね。これは形式的にはラムダ式で次の計算 ((lambda (m) (if (= m 0) 1 2)) 0) を行っている事と同じになると思います。

pikacha
質問者

お礼

回答をありがとうございます。 おっしゃる通りSICPです。 質問文に対するご指摘ありがとうございます。 consの定義の最後にdispatchが抜けていました。 なるほど! dispatchという手続きを返していますが、それは内部的には (define dispatch (lambda (m)   (cond ((= m 0) x)      ((= m 1) y)      (else (error "Argument not 0 or 1 -- CONS" m))))) という定義になっていますから、その内部定義においてdispatchを返すということは (lambda (m)   (cond ((= m 0) x)      ((= m 1) y)      (else (error "Argument not 0 or 1 -- CONS" m)))) を返すことに他ならない訳ですね! 詳しい丁寧な説明をありがとうございました。

関連するQ&A

  • 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の使い方を間違っているんでしょうか?

  • 落ちてしまいます

    無限ストリームなのですが、 (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を定義する段階で落ちてしまうようです。どうも遅延評価がうまくいってないようです。どうしたらよいでしょうか?どなたか助けてください。

  • schemeです

    次の問題がわかりません。 月と日を入力すると 4月1日から何日目の日であるかを返す手続きを作成せよ。 ただし 今年の 4月1日から12月31日までが正しく計算できれば よい >(define (tukihi x y) (cond ((= (- x 4) 0) y) ((= (- x 4) 1) (+ 30 y)) ((= (- x 4) 2) (+ 60 y 1)) ((= (- x 4) 3) (+ 90 y 2)) ((= (- x 4) 4) (+ 120 y 2)) ((= (- x 4) 5) (+ 150 y 3)) ((= (- x 4) 6) (+ 180 y 3)) ((= (- x 4) 7) (+ 210 y 4)) ((= (- x 4) 8) (+ 240 y 4)))) こうしたのですが、一応値は返ってくるのですがこれではだめなようです。 日数計算はコンピュータに任せて式を書けばよいだけと言われたんですが、どうすればいいかわかりません。アドバイスおねがいします

  • Schemeのコンストラクタの引数は?

    Schemeのコンストラクタの引数は? 以前、Schemeについて質問させて頂いた者です。 まず、4つのフィールドx,y,delta-x,delta-yから成るBALL構造体と、2つのフィールドx,yから成るPOSN構造体を定義しました。 (define-struct BALL (x y delta-x delta-y)) (define-struct POSN (x y)) そして、そこから課せられた内容が 「BALL構造体データ a-ball から座標位置を取り出し、POSN構造体データとして返す関数 get-ball-posn を定義せよ。(make-posnを利用せよ)」 というものです。 私はまず、以下のように書いてみました。 (define a-ball (make-BALL x y delta-x delta-y)) ・・・(☆) (define (get-ball-posn a-ball) (make-POSN (BALL-x a-ball) (BALL-y a-ball))) そして (get-ball-posn (make-BALL 10 20 30 40)) のように実行してみると、 (☆)の部分の、x,y,delta-x,delta-yは定義されてない、という内容のエラーが返って来ました。 仕方ないので、その部分を'x 'y 'delta-x 'delta-yにそれぞれ直して再び実行してみると、 (make-POSN 10 20) と正しい答えが返ってきました。 色々試した結果、“コンストラクタの引数は、変数を取ることが出来ない”という結論に至りました。 これは正しいのですか? また、上記の'x 'y 'delta-x 'delta-yのままでは、正しい答えは返ってくるものの、どうも納得いきません。模範解答はどうすれば良いのでしょうか。 何とぞご教授の程、宜しくお願いします。

  • 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時間以上考えていますがまったく出てきません。。 どうかよろしくお願いいたします。 最後に、長文の質問を最後までお読みくださりありがとうございました。

  • Shemeで

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

  • Schemeのプログラミング うるう年関連

    Schemeのプログラミング うるう年関連 Schemeで、 「(1)閏年かどうかを判定する関数leap?(number -> boolean)を定義した後、(2)○年○月の日数は何日かを求める関数num-of-days(number number -> number)を定義せよ」 という内容の課題を出されたのですが、(例えば 2009年の7月→31日 2012年の2月→29日) どうもエラーが出て実行できません。 自分は以下のように組みました。 ;;(1)の関数 (define (leap? year) (cond [(= (remainder year 400) 0) #t] [(and (= (remainder year 4) 0)(> (remainder year 100) 0)) #t] [else #f] ) ) ;;(2)の関数 (define (num-of-days year month) (cond [(and (= month 2)(= (leap? year) #t)) 29] [(and (= month 2)(= (leap? year) #f)) 28] [(or (= month 1)(= month 3)(= month 5) (= month 7)(= month 8)(= month 10) (= month 12)) 31] [else 30] ) ) これを例えば (num-of-days 2008 4) や (num-of-days 1995 12) などとして実行すると、それぞれ30,31という正しい値を返してくれるのですが、 (num-of-days 2008 2) や (num-of-days 1995 2) など、閏年・非閏年に関係無く、2月が絡むと =: expects type <number> as 1st argument, given: false; other arguments were: true というエラーを吐いてしまいます。 何度も見直しましたが、どこが間違っているのか見付けきれません…。どなたか間違いを指摘して頂けると幸いです。

  • C言語での関数形式マクロの使い方

    前の質問No.300834(関数形式マクロと空白の質問)と関連します。 関数形式マクロで、引数として入れるものは、変数でなくて型名でも構わないのでしょうか。 例えば、 #define mymul(t,x,y) ((t)(x)*(t)(y)) と定義すると、 mymul(int, 5.0, 3.5) と呼び出すと、 ((int)(5.0)*(int)(3.5)) に置き換える、 (intでキャストした 5.0 と、intでキャストした 3.5 をかける) というのは可能でしょうか。 あと、関数形式マクロの呼び出しは、実行部分でなくてもよいのでしょうか。関数頭部(関数の本体の前の部分)で呼び出せますか。 例えば #define ARGUMENT3(t1,v1,t2,v2,t3,v3) ¥ (t1 v1, t2 v2, t3 v3) #define a_func b_func ARGUMENT3 と定義しておいて、 関数を定義するときに、 int a_func(int,x, char*,cp, int**,ypp) { ・・・・ } こんなことをすると、 int b_func(int x, char *cp, int **ypp) { ・・・・ } に置き換わりますか? もし、ARGUMENT3の定義を、ARGUMENT3の後の括弧の中のカンマのつけ方を変えて、 #define ARGUMENT3(t1 v1,t2 v2,t3 v3) ¥ (t1 v1, t2 v2, t3 v3) とし、 int a_func(int x, char* cp, int** ypp) { ・・・・ } こうすると、先ほどのようなb_funcの関数頭部に変換することは出来ませんか? (関数形式マクロでこのような空白の入れ方をしてよいのでしょうか。)

  • pythonのargparseで引数の数の可変

    現在、pythonのプログラムで、コマンドにオプションを付けて、それによって戻ってくる結果を表示するようにしたいと思い作成しています。 それで、次のようなプログラムを作ることができました。 $ python pythonProg.py -s 5 5 5 5 5 5 5+5+5 == 15 (githubでの現在のソースコード内容) https://github.com/KenjiMaehara/raspberryPiTCPClient/blob/testBranch20170201_02/testSpi161101/pythonProg.py 現在、-sのオプションと3つの引数x,y,zを渡すと、x,y,zの総和を表示するようにしているのですが、 引数の数を2つにした場合、次のようなエラーが出てきます。 $ python pythonProg.py -s 5 5 usage: pythonProg.py [-h] [-v | -q | -s] x y z pythonProg.py: error: too few arguments ”argumentsが少ない”というエラーなのでしょうが、引数を3つ以外でも総和を計算できるようにすることはできますでしょうか? 現在のソースコード内容な下記のような感じです。 (pythonProg.pyのファイル内容) import argparse parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() group.add_argument("-v", "--verbose", action="store_true") group.add_argument("-q", "--quiet", action="store_true") group.add_argument("-s", "--sum", action="store_true") parser.add_argument("x", type=int, help="the base") parser.add_argument("y", type=int, help="the exponent") parser.add_argument("z", type=int, help="the exponent") args = parser.parse_args() answer = args.x+args.y answer02 = args.x+args.y+args.z (中略) どうぞ、宜しくお願い致します。

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

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

専門家に質問してみよう