結城浩のSICP日記 RSSフィード

2006-05-09

複素数計算 複素数計算 - 結城浩のSICP日記 を含むブックマーク

Danさんの%w(Complex Number).reverseからお題をいただく。

(use math.const)

(print "pi                 = " pi)
(print "e                  = " e)
(print "(expt e (* +i pi)) = " (expt e (* +i pi)))
(print "(exp (* +i pi))    = " (exp (* +i pi)))
(print "(log -1)           = " (log -1))

実行結果です。

pi                 = 3.141592653589793
e                  = 2.718281828459045
(expt e (* +i pi)) = -1.0+1.2246063538223773e-16i
(exp (* +i pi))    = -1.0+1.2246063538223773e-16i
(log -1)           = 0.0+3.141592653589793i
  • iは使えないけれど、+iと書けばよい。
  • powerじゃなくてexptなんだね。
  • 当然のように複素数が使えるのがすごいなあ。

追記:

shiroさんから「浮動小数点数の末尾に注目」というコメントをいただきました。(その道の人は)ご注目ください。

呼ぶたびに0, 1, 2, ...を返す関数 呼ぶたびに0, 1, 2, ...を返す関数 - 結城浩のSICP日記 を含むブックマーク

問題

呼ぶたびに0, 1, 2, ...を返す関数fを定義せよ。ただしトップレベルの変数に代入してはいけない。

解答

(間違い)
<s>(define (f)
  (define x 0)
  (set! f (lambda () (set! x (+ x 1)))) x)</s>

torusさんとh-hiraiさんからのご指摘で以下修正。感謝です。

(define (f)
  (define x 0)
  (set! f
    (lambda () (set! x (+ x 1)) x))
  x)

実行結果です。

(f)
;=> 0

(f)
;=> 1

(f)
;=> 2

追記:

torusさんからset!しない解答をいただきました。感謝! この解で、結城のletの理解が間違っていたことを理解しました。大感謝。

;torusさんより(1)
(define f
  (let ((x 0))
    (lambda ()
      (let ((prev x))
        (set! x (+ x 1))
        prev))))

別解。

;torusさんより(2)
(define (make-f)
  (define x 0)
  (lambda ()
    (let1 prev x
      (set! x (+ x 1))
      prev)))
  (define f (make-f))

ええと、上の(1)は以下のようにしてもいいですよね?

(define f
  (let ((x 0))
    (lambda ()
      (set! x (+ x 1))
      (- x 1))))

[]p.137:評価順序 p.137:評価順序 - 結城浩のSICP日記 を含むブックマーク

ひげぽんさんが問題3.8のあたりをやっていたので、私も考えた。

問題3.8

(define (f x)
  (set! f (lambda (x) 0))
  x)

[]コマンドラインとmain コマンドラインとmain - 結城浩のSICP日記 を含むブックマーク

Gaucheではmainという手続きがあったらそれを呼び出します。

; a.scm
(define (main args)
  (print args)
  (print *argv*))
> gosh a.scm 1 2 3
(a.scm 1 2 3)
(1 2 3)

higeponhigepon2006/05/09 09:58手続きをset!ですか。参りました。思いつかなかったです。

torustorus2006/05/09 15:14f は「トップレベルの変数」でもあるような気が……。でも、わざわざ f を set! しなくてもいいんじゃないでしょうか?
あ、あとついでですが、a.scm のコメントが a.asm になっています。一瞬このあとにアセンブリコードが続くのかと期待してしまいました(笑)。

hyukihyuki2006/05/09 15:53確かに、トップレベルの変数ですね…あとで、こそっと問題文を書き換えておきます(^_^;
fをset!しないと、毎回xが初期化されませんか?

hyukihyuki2006/05/09 15:57a.asmも直しておきました。ありがとうございます!

torustorus2006/05/09 16:19あ、はい。たしかにこのままだと f を set! しないとダメなんですが、let を使うと大丈夫です。
(define f
(let ((x 0))
(lambda ()
(let ((prev x))
(set! x (+ x 1))
prev))))
あるいは、let を使いたくなければ、ちょっと回りくどいですが、クロージャを返す関数をつかってこんな風にも書けます。
(define (make-f)
(define x 0)
(lambda ()
(let1 prev x
(set! x (+ x 1))
prev)))
(define f (make-f))
あ、あと、set! の戻り値はたしか未定義なので、使わない方がいい気がします。

shiroshiro2006/05/09 18:14複素数とは関係ないんですが、各処理系の表示する浮動小数点数の末尾の方の桁を見比べてみると(その道の人には)興味深いかもしれません。
Gaucheの場合、piやeは小数点以下15桁まで表示しているのに、(exp (* -i pi)) の結果の虚数部はなぜ小数点以下16桁まで表示されているのか、とか。

hyukihyuki2006/05/09 18:27torusさん、私の解のレイアウトが悪いのですが、いちおうset!の戻り値は使っていません。その後のxが使われる戻り値ですので。

h-hiraih-hirai2006/05/09 18:52勘違いだったら申し訳ないです。
(define (f)
(define x 0)
(set! f (lambda () (set! x (+ x 1)) x)))
ということじゃないかなー? とか…

hyukihyuki2006/05/09 19:17ふみぃ! h-hiraiさん、感謝です。内側にもset!がありましたね!

トラックバック - http://sicp.g.hatena.ne.jp/hyuki/20060509