ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

λの会コミュの初心者な質問ですが…

  • mixiチェック
  • このエントリーをはてなブックマークに追加
Emacs Lisp で以下のように、しました。
-----------------------------------
(setq arg '(x y))
(x y)

(setq val '(7 10))
(7 10)

(setq env 'env)
env

(defun bind (arg val env)
(cons env (bind1 arg val)))
bind

(defun bind1 (arg val)
(cond
((and (null arg) (null val)) nil)
(t
(cons (cons (car arg) (car val))
(bind1 (cdr arg) (cdr val))))))
bind1

(bind arg val env)
(env (x . 7) (y . 10))
-----------------------------------
プロシージャをbindとbind1に分けずに書く方法はあるでしょうか?

コメント(11)

(defun bind (arg val env)
(let ((f
(lambda (arg val f)
(cond
((and (null arg) (null val)) nil)
(t
(cons (cons (car arg) (car val))
(funcall f (cdr arg) (cdr val) f)))))))
(cons env (funcall f arg val f))))

脳味噌使う気ないのでこんなので。
emacs全般で動くかどうかはしらないですけど
21.4.1 on debianでは動いているっぽかったです。
Emacs Lisp だと再帰の深さに制限があるので反復的
にやると良いと思います。

(setq arg '(x y)
val '(7 10)
env 'env)

(defun bind (env arg val)
(let ((lst nil))
(while (and arg val)
(push (cons (pop arg) (pop val)) lst))
(cons env lst)))

(bind env arg val)

=> (env (y . 10) (x . 7))
もし x, y の順序に意味があるのでしたら
(cons env lst) => (cons env (nreverse lst))
のように書き換えてください。

…とここまで書いてまた気がつきましたが
(require 'cl) して flet で関数内で関数を
定義したいという話だったのでしょうか?
お答えありがとうございます。

elispでschemeのインタプリタを書いてある文献がありまして、その文献には環境のモデルが具体的には書かれていませんでした。
そこで、自分で考えてみたのですが、もう少しシンプルに書けるはずと思ったわけでした。
ですので、x,yの順番に意味は無いです。
schemeでは以下のようなことはできないのでしょうか?
また、funcallに相当するものは無いのでしょうか?

gosh> (define plus '+)
plus
gosh> (plus 1 2)
*** ERROR: invalid application: (+ 1 2)
schemeではこのようにします。

gosh> (define plus +)
plus
gosh> (plus 1 2)
3

+にクオートがついてないことに注意。

+というシンボルが手元にあって、それに結びついた手続きを手に入れたい場合はevalを使う必要があります。

gosh> (eval '+ (scheme-report-environment 5))
#<subr +>

なぜこんなめんどくさいことが必要かというと、 + というシンボルと関数の実体 (gaucheでは #<subr +> と表示されるもの) との結び付きはグローバル環境によって決まるため、「どのグローバル環境を参照するか」を明示する必要があるからです。(scheme-report-environment 5) というのは R5RS で規定されているグローバル環境ということです。他には例えば、現在インタラクティブな評価に使っているグローバル環境を得る (interaction-environment) というのもあります。

次の例はinteraction-environmentでの + の定義を置き換えるものです。定義を置き換えても、(scheme-report-environment 5) での + の定義が変わっていないことを示します。

gosh> (define + (lambda (a b) (list a b)))
+
gosh> (eval '+ (interaction-environment))
#<closure +>
gosh> (+ 1 2)
(1 2)
gosh> ((eval '+ (interaction-environment)) 1 2)
(1 2)
gosh> ((eval '+ (scheme-report-environment 5)) 1 2)
3
なるほど[']を付けてはいけないんですね。

関数名(ev-appl-did-operator)を変数(continue)に格納して、それを呼び出そうとして以下のようにした結果、うまく行かない理由がわかりました。

gosh> (set! continue 'ev-appl-did-operator)
([']がある)

そこで以下のように[']を取り除いて実行した結果うまく行きました。

gosh> (set! continue ev-appl-did-operator)
([']が無い)
gosh> (continue)
...

解説いただき、助かりました。m(_ _m
http://mitpress.mit.edu/sicp/code/ch5-eceval.scm
のレジスタ計算機的記述をschemeに変換してみました
http://www.pkan.org/~ufo/wiliki.cgi?%c6%b0%a4%ab%a4%ca%a4%a4scheme%a4%cb%a4%e8%a4%ebscheme%a5%a8%a5%df%a5%e5%a5%ec%a1%bc%a5%bf

補助部分は、SICPにのページで公開されていて、レジスタ計算機的記述をエミュレートするのに使う部分です。
そのまま、main部分のサポートに使えると想定して取り込みました。

main部分を書きました。
loadしてrunすると変数expを評価します。
(debug中なのでコードは汚いです)
レジスタ計算機と同じ動きを再現しているはずなのにさっぱり動いてくれません(汗
SICPでこの辺のことを知っている方や、判る方がいらっしゃったら、何がおかしいのか教えていただけると助かりますm(_ _;m
自己レスすいません。


どうやら、まず、スタックがおかしかったようです。

pushは普通にできました。
----
(define (push! arg) (set! stack (cons arg stack)))
----
gosh> (push! 1)
(1)
gosh> (push! 2)
(2 1)


問題はpopでした。
以下の定義でpop!を実行すると2がaに代入されるはずが1が代入されます。
----
(define (pop! arg) (set! arg (car stack)) (set! stack (cdr stack)))
----
gosh> (pop! a)
(1)
gosh> a
1


そこで、逐次化するためlambdaを加えた記述に変えたところ、正常にaに2が代入されました。
----
(define pop! (lambda (arg)
(set! arg (car stack))
(set! stack (cdr stack))))
----
gosh> (pop! a)
(1)
gosh> a
2

(なぜかこの場面ではbeginは有効ではありませんでした。)

他にも逐次化していない箇所がたくさんあります。
とりあえず、そこを修正してみます。

schemeにわざわざbeginが何故あるのだろう?と思っていましたが、それがすこし解消されました。

ところで、この特徴はscheme特有の特徴でしょうか?
それともlisp一般の特徴でしょうか?
いや、何か勘違いされてるような気がします。

(define pop! (lambda (arg) (set! arg (car stack)) ...))

この式でset!されるのは「ローカル変数arg」の値であって、

(pop! a)

と呼び出した場合に「変数a」自体は変更されません(変更されたら困る)。値呼びcall by valueというもので、引数で受け渡されるのは変数の「値」であって変数そのものではないからです。これはLisp一般だけでなく、C言語やPerl、Python、Rubyなど現代の多くのプログラミング言語に共通の仕様です。

aの値が変わってしまっているケースでは、他のところでグローバルなaに触ってないか、タイプミスが無いかなとを確認して下さい。本来変わらないはずです。

また、(define (pop! arg) ...) と (define pop! (lambda (arg) ...)) は「完全に等価」ですので、両者で動作が異なるということはあり得ません。

なお、(pop! a) と呼び出して変数aそのものの値を変えたい場合はマクロを使います。
結局、関数にするのをやめて
(set! a (car stack))
(set! stack (cdr stack))
と直接書くことで問題を回避しました。
(ですのでマクロは使ってません。。)

> C言語やPerl、Python、Rubyなど現代の多くのプログラミング言語に共通の仕様です。
お恥ずかしい限りです。
こんな、基礎的な間違いをするとは(滝汗

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

λの会 更新情報

λの会のメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。

人気コミュニティランキング