(sicp36)sicp m3.6 ~ 3.8

;;m3.6
;;線形合同法で乱数をつくる。
(define (rand A B M)
  ;M > A > 0
  ;M > B > 0
  ;B, M が互いに素
  ;A - 1 がmの持つすべての素因数で割り切れる
  ;M%4=0 => (A-1)%4=0
  ;M > Xn
  ;;面倒なのでA,B,Mの値を以下からパクります><
  ;;http://wiki.fdiary.net/vipprog/?%C0%FE%B7%C1%B9%E7%C6%B1%CB%A1(C%B8%C0%B8%EC%C8%C7)
  (lambda (x)
    (lambda (c)
      (let ((update (lambda ()
                      (set! x (remainder (+ (* A x) B) M)))))
            (cond ((eq? c 'reset)  ;;reset
                   (lambda (new-v) (set! x new-v)))
                  ((eq? c 'generate) ;;次の値に変更
                   (update)
                   x)
                  ((eq? c 'random-in-range) ;;範囲を指定した乱数
                   (lambda (low high)
                     (let ((renge (- high low)))
                       (+ low (* renge (/ (update) M))))))
            (else (error "not found")))))))

(define r ((rand 997 1 65536) 12345))

(map (lambda (x) (r 'generate)) (iota 5))
((r 'reset) 12345)
(map (lambda (x) (r 'generate)) (iota 5))
;; 20:user> => (52734 15927 19508 50821 9210)
;; 21:user> => 12345
;; 22:user> => (52734 15927 19508 50821 9210)

(map (lambda (x) ((r 'random-in-range) 1.0 1.5)) (iota 5))
;; 36:user> => (1.0559310913085937 1.2633056640625 1.0157546997070312 1.2074432373046875 1.3209152221679687)
;;普通は絶対こんなやり方しないかも?(もっと関数とかに分けた方がよかったかもしれません><)


;;m3.7
;;passwort修正機能をつける。
(define (make-account balance original-pass)
  (let ((c 7) (pass (list original-pass)))
    (define (withdraw amount)
      (if (>= balance amount)
          (begin (set! balance (- balance amount))
                 balance)
          "Insufficient funds"))
    (define (deposit amount)
      (set! balance (+ balance amount))
      balance)
    (define (add-password newp) ;;ここ追加した
      (unless (memq newp pass)
        (set! pass (cons newp pass)))
               dispatch)
    (define (call-the-cops x)    "call-the-cops")
    (define (dispatch p m)
      (if (memq p pass) ;;ここ変更した
          (begin (set! c 7)
                 (cond ((eq? m 'withdraw) withdraw)
                       ((eq? m 'deposit) deposit)
                       ((eq? m 'add-password) add-password) ;;ここ追加した
                       (else (error "Unknown request -- MAKE-ACOUNT" m))))
          (begin (if (= c 0)
                     call-the-cops
                     (begin (set! c (- c 1))
                            (lambda (x) "Incorrect pasword!"))))))
    dispatch))

(define (make-joint acc p newp)
  ((acc p 'add-password) newp))

(define peter-acc (make-account 100 'open-sesame))
(map (lambda (x) ((peter-acc 'open-sesame 'deposit) 10)) (iota 5))
(define paul-acc
  (make-joint peter-acc 'open-sesame 'rosebud))
(map (lambda (x) ((paul-acc 'rosebud 'withdraw) 20)) (iota 5))

;; 34:user> => peter-acc
;; 35:user> => (110 120 130 140 150)
;; 36:user> => paul-acc
;; 37:user> => (130 110 90 70 50)

;;m3.8
(define f
  (let1 state 0 
        (lambda (n)
          (let1 prev state
                (set! state n)
                prev))))

(+ (f 0) (f 1))
;; 40:user> => 0

(map (lambda (x) (cons x (f x))) (iota 5))
;; 43:user> => ((0 . 1) (1 . 0) (2 . 1) (3 . 2) (4 . 3))