SICP

SICP読書女子会 2.2.3 (#21,22,23,24)

More than 1 year has passed since last update.

2.2.3 集積

SICP/2.2.3.scm at master · cocodrips/SICP

(display "*********2.2.3**********")
(newline)

;; 標準インタフェース
;; 奇数の葉の二乗の合計を計算
(define (sum-odd-squares tree)
    (cond 
        ((null? tree) 0)
        ((not (pair? tree)) (if (odd? tree) (square tree) 0))
    (else 
        (+ (sum-odd-squares (car tree))
    (sum-odd-squares (cdr tree))))))

;;by @hioさん
; p.40, 1.2.2 木の再帰
(define (fib n)
    (define (iter a b count)
        (if
            (= count 0)
            b
            (iter (+ a b) a (- count 1))))
    (iter 1 0 n))

;; フィボナッチ数のうち、偶数のものをかえすリストを構築する
(define (even-fibs n)
    (define (next k)
        (if (> k n)
            ()
            (let ((f (fib k)))
                (if 
                    (even? f)
                    (cons f (next (+ k 1)))
                    (next (+ k 1))))))
(next 0))
(display (even-fibs 10))
(newline)
;(0 2 8 34)

;; Filter
(define (filter predicate sequence)
    (cond 
        ((null? sequence) ())
        ((predicate (car sequence)) 
            (cons (car sequence)
            (filter predicate (cdr sequence))))
    (else (filter predicate (cdr sequence)))))

(display (filter odd? (list 1 2 3 4 5)))
(newline)

;; 集積 accumulate
(define (accumulate op initial sequence)
    (if 
        (null? sequence)
        initial
        (op (car sequence) (accumulate op initial (cdr sequence)))))

(display (accumulate + 0 (list 1 2 3 4 5)))(newline)
;15
(display (accumulate * 1 (list 1 2 3 4 5)))(newline)
;120
(display (accumulate cons () (list 1 2 3 4 5)))(newline)

;(1 2 3 4 5)

よくわからなくなるのでメモ:

accumulate

(accumulate f(array[i], 今までのaccumulate結果) 初期値 array) 
-> 初期値にmergeされてく

map

(map f(array[i]) array)
-> 配列が返る

2.33

(display "======Ex.2.33======")
(newline)
;練習問題 2.33: 基本的なリスト操作のいくつかを集積として定義
;したものを以下に示す。欠けている式を埋めて、完成させよ。

(define (map p sequence)
    (accumulate (lambda (x y) (cons (p x) y)) () sequence))

(define (double x)
   (* 2 x))
(display (map double (list 1 2 3 4 5))) 
(newline)
;(2 4 6 8 10)

(define (append seq1 seq2)
    (accumulate cons seq1 seq2))
(display (append (list 1 2 3) (list 4 5 8))) 
(newline)
;(4 5 8 1 2 3)
(define (length sequence)
    (accumulate (lambda (_ x) (+ x 1)) 0 sequence))
(display (length (list 1 2 3)) )
(newline)
;3

2.34

(display "======Ex.2.34======")
(newline)

(define (horner-eval x coefficient-sequence)
    (accumulate
        (lambda (this-coeff higher-terms) (+ (* higher-terms x) this-coeff))
        0
        coefficient-sequence))

(display (horner-eval 2 (list 1 3 0 5 0 1)))
;; 79

Ex 2.35

(display "======Ex.2.35======")
(newline)

(define (count-leaves t)
    (accumulate + 0 (map (lambda (x) 1) (enumerate-tree t))))

(define tree (cons (list 1 2) (list 3 4)))
(display (count-leaves tree))
(newline)
; 4

(define tree (cons (list 1 2) (list 3 4 5)))
(display (count-leaves tree))
(newline)
;5

@hioさんの実装|再帰的に足してく


(define (count-leaves t)
    (accumulate
        +
        0
        (map
            (lambda (x)
                (if
                    (pair? x)
                    (count-leaves x)
                    1))
            t)))

Ex 2.36

(display "======Ex.2.36======")
(newline)

(define (accumulate-n op init seqs)
(if (null? (car seqs))
    nil
    (cons (accumulate op init (map car seqs)) (accumulate-n op init (map cdr seqs)))))


(define s (list (list 1 2 3) (list 4 5 6) (list 7 8 9) (list 10 11 12)))
(display (accumulate-n + 0 s))

Ex 2.37

(display "======Ex.2.37======")
(newline)

;(define (map proc items)
;    (if (null? items)
;    nil
;    (cons (proc (car items)) (map proc (cdr items)))))
(define (dot-product v w)
    (accumulate + 0 (_map * v w)))

(define m1 (list (list 1 2 3 4) (list 4 5 6 7)))
(define v1 (list 1 2))
(define v2 (list 10 20))


(display (dot-product v1 v2)) 
(newline)
;50

(define (matrix-*-vector m v)
    (_map (lambda (m_i) (dot-product m_i v)) m)) ;; By @hioさん

(display (matrix-*-vector m1 v2))
(newline)
;(50 140)


(define (transpose mat)
    (accumulate-n cons nil mat))
(display (transpose m1))
(newline)
;((1 4) (2 5) (3 6) (4 7))


;(define (matrix-*-matrix m n)
;(let ((cols (transpose n)))
;(map ⟨??⟩ m)))
;cols???

Ex 2.38

(display "======Ex.2.38======")
(newline)

;練習問題 2.38: accumulate 手続きは、列の最初の要素と、右の
;すべての要素を組み合わせた結果とを組み合わせるため、foldright
;としても知られている。fold-left というものもあり、これ
;は fold-right に似ているが、要素の組み合わせを逆方向に行うと
;いう点が違う。
(define (accumulate op initial sequence)
    (if (null? sequence)
        initial
        (op (car sequence) (accumulate op initial (cdr sequence)))))

(define fold-right accumulate)

(define (fold-left op initial sequence)
    (define (iter result rest)
        (if (null? rest)
            result
        (iter (op result (car rest))
            (cdr rest))))
(iter initial sequence))

(display (fold-right / 1 (list 1 2 3)))     (newline) 
; (/ 1 (/ 2 (/ 3 1)))
; 2 / 3
(display (fold-left / 1 (list 1 2 3)))      (newline)
; (/ (/ (/ 1 2) 2 ) 3)

(display (fold-right list nil (list 1 2 3)))(newline)
;(1 (2 (3 ())))
(display (fold-left list nil (list 1 2 3))) (newline)
;(((() 1) 2) 3)

;; 可換な演算ならright/fold-leftで結果がおなじになる

Ex 2.39

(display "======Ex.2.39======")
(newline)
;練習問題 2.39: 以下の reverse(練習問題 2.18) 手続きの定義を、練
;習問題 2.38の fold-right と fold-left によって完成させよ。
(define (reverse sequence)
    (fold-right (lambda (x y) (append (list x) y)) (list) sequence))

(define l (list 1 3 5))
(display (reverse l))
(newline)
;(5 3 1)

(define (reverse sequence)
    (fold-left (lambda (x y) (cons y x)) nil sequence))
(display (reverse l))
(newline)
;(5 3 1)

マップのネスト

;; マップのネスト
(define (enumerate-interval low high)
    (if 
        (> low high)
        nil
    (cons low (enumerate-interval (+ low 1) high))))
(display (enumerate-interval 2 7))
(newline)


(define n 3)
(display (accumulate
    append nil (map (lambda (i)
        (map (lambda (j) (list i j))
            (enumerate-interval 1 (- i 1))))
        (enumerate-interval 1 n))))
(newline)
;((3 1) (3 2) (2 1))
(load "../1.2/smallest-divisor.scm")
(define (prime-sum? pair)
    (prime? (+ (car pair) (cadr pair))))

(define (flatmap proc seq)
    (accumulate append nil (map proc seq)))
;; [proc(seq[0]), proc(seq[1]) .....] みたいなのが出来る?

(define (make-pair-sum pair)
    (list (car pair) (cadr pair) (+ (car pair) (cadr pair))))

(define (prime-sum-pairs n)
    (map make-pair-sum
        (filter prime-sum? (flatmap
            (lambda (i)
            (map (lambda (j) (list i j))
            (enumerate-interval 1 (- i 1))))
        (enumerate-interval 1 n)))))

(display (prime-sum-pairs 3))
(newline)
;((3 2 5) (2 1 3))

Ex 2.40

(display "======Ex.2.40======")
(newline)
;整数 n に対し、1 ≤ j < i ≤ n となるペア (i, j) の
;列を生成する手続き unique-pairs を定義せよ。unique-pairs を
;使って上の prime-sum-pairs の定義を簡単にせよ。

;; 自分の
(define (unique-pairs n)
    (define (itr base i stack)
        ;(print base ":" i "\n")
        (cond 
            ((>= base n) stack)
            ((> i n) (itr (+ base 1) (+ base 2) stack))
            (else (itr base (+ i 1) (cons (list base i) stack)))))
(itr 1 2 nil))

(display (unique-pairs 4))
(newline)
;((3 4) (2 4) (2 3) (1 4) (1 3) (1 2))

;(define (accumulate op initial sequence)
(define (prime-sum-pairs n)
    (accumulate 
        (lambda (i acc) 
            (if (prime? (+ (car i) (car (cdr i))))
                (cons i acc)
            acc))
        nil
    (unique-pairs n)))

(display (prime-sum-pairs 3))
(newline)
;((2 3) (1 2))

;; hioさんの
;(define (unique-pairs n)
;    (flatmap 
;        (lambda (i) 
;                (map (lambda (j) (list i j)) (enumerate-interval 1 (- i 1))))
;        (enumerate-interval 1 n)
;    )
;)
;(display (unique-pairs 4))
;(newline)
;((4 1) (4 2) (4 3) (3 1) (3 2) (2 1))
;2.41が小さい順にならんでいるというのが条件として満たさないと
;正しい挙動をしない実装をしてしまったため、コメントアウト

Ex 2.41

(display "======Ex.2.41======")
(newline)
;ある整数 n 以下の異なる正の整数が大小順に並ん
;だ三つ組 i, j, k の中で、合計がある整数 s となるものすべてを見
;つける手続きを書け。


(define (first p) (car p))
(define (second p) (car (cdr p)))
(define (unique-tri n s)
    (define (add-pattern p stack)
        (define (itr p i stack)
            ;(print p i stack)
            (cond 
                ((> i n) stack)
                ((>= (second p) i) (itr p (+ i 1) stack))
                ((> (second p) n) stack)
                (else 
                    (if 
                        (= (+ (first p) (second p) i) s)
                        (itr p (+ i 1) (cons (list (first p) (second p) i) stack))
                        stack))
            )
        )
        (itr p 1 stack)
    )


    (accumulate
        add-pattern
        nil
        (unique-pairs n))

)
(display "わたしの:")
(display (unique-tri 5 10))(newline)
;わたしの:((1 4 5))
;全ペア((3 4 5) (2 4 5) (2 3 5) (2 3 4) (1 4 5) (1 3 5) (1 3 4) (1 2 5) (1 2 4) (1 2 3))

(display "hioさんの:")
(define (unique-triples n)
    (flatmap
        (lambda (i)
            (flatmap
                (lambda (j)
                    (map
                        (lambda (k)
                            (list i j k))
                        (enumerate-interval 1 (- j 1))))
                (enumerate-interval 1 (- i 1))))
        (enumerate-interval 1 n)))
(display (unique-triples 5))
(newline)
;hioさんの:((5 4 1) (5 4 2) (5 4 3) (5 3 1) (5 3 2) (5 2 1) (4 3 1) (4 3 2) (4 2 1) (3 2 1))
; なるほど、こういうことなのね;; 納得;;

Ex2.42

8Queen

(display "======Ex.2.42======")
(newline)

(define (make-position row col)
   (cons row col))

(define (position-row position)
   (car position))

(define (position-col position)
   (cdr position))

(define empty-board nil)

(define (queens board-size)
    (define (queen-cols col)
        ;(print col)
        (if (= col 0)
            (list empty-board)
            (filter
                (lambda (positions) (safe? col positions))
                (flatmap
                    (lambda (rest-of-queens)
                        (map 
                            (lambda (new-row) (adjoin-position new-row col rest-of-queens)) ;;kだけbitかけたやつをかえすかんじ?
                            (enumerate-interval 1 board-size) ; [1...k]
                        ))
                    (queen-cols (- col 1))))))
(queen-cols board-size))

(define (safe? col positions) 
    (let ((kth-queen (list-ref positions (- col 1)))
     (other-queens (filter (lambda (q)
                             (not (= col (position-col q))))
                           positions)))
    (define (valid-position? p1 p2)
        ;(print "valid-position:" p1 p2)
        (not (or 
            (= (position-row p1) (position-row p2))
            (= (position-col p1) (position-col p2))
            (= 
                (abs (- (position-row p1) (position-col p1)))
                (abs (- (position-row p2) (position-col p2)))))))

    (define (iter queen-position board)
     (or (null? board)
         (and (valid-position? queen-position (car board))
              (iter queen-position (cdr board)))))
   (iter kth-queen other-queens))
)

(define (adjoin-position row col positions)
    (append (list (make-position row col)) positions))

(display (queens 0))
(newline)

(display (queens 4))
(newline)
;(((4 . 1) (1 . 2) (3 . 3) (2 . 4)) ((4 . 1) (2 . 2) (1 . 3) (3 . 4)) ((3 . 1) (2 . 2) (4 . 3) (1 . 4)) ((2 . 1) (4 . 2) (3 . 3) (1 . 4)))

(display (queens 5))
(newline)
;(((3 . 1) (5 . 2) (2 . 3) (4 . 4) (1 . 5)) ((4 . 1) (2 . 2) (5 . 3) (3 . 4) (1 . 5)) ((5 . 1) (3 . 2) (1 . 3) (4 . 4) (2 . 5)) ((5 . 1) (2 . 2) (4 . 3) (1 . 4) (3 . 5)))

http://www.billthelizard.com/2011/06/sicp-242-243-n-queens-problem.htmlを参考にしつつ・・・。

2.42 やりなおし

Schemeで考えると難しいのでとりあえずまずPythonでかいた

class QueenN():
    def __init__(self):
        pass

    def create(self, n):
        boards = [[-1] * n]
        for row in range(n):
            boards = self.add_row(row, n, boards)

        print n, "Pattern:", len(boards)
        for board in boards:
            for col in board:
                for i in xrange(n):
                    if i == col:
                        print "Q",
                    else: 
                        print "_",
                print 
            print 


    def add_row(self, row, n, boards):
        next_boards = []
        for board in boards:
            for col in range(n):
                array = board[:]
                if self.is_safe(board, row, col):
                    array[row] = col
                    next_boards.append(array)

        return next_boards

    def is_safe(self, board, row, col):
        # cols
        if col in board:
            return False

        # x
        for i in range(row):
            if (i - board[i]) == (row - col):
                return False
            if abs(board[i] + i) == (row + col):
                return False
        return True

if __name__ == '__main__':
    queen = QueenN()
    for i in range(3, 9):
        queen.create(i)

上をふまえて書き直し

;; 2016/10/19 #24
(newline)
(print "===Ex 2.42 もういっかい===")
(define (queens board-size)
    (define (queen-cols k)
        (if (= k 0)
            (list empty-board)
            (filter
                (lambda (positions) (safe? k positions))    ;; 渡ってきたboardが正しい配置か
                (flatmap                                    ;; 全列挙して、↑を満たすものだけ残す
                    (lambda (rest-of-queens)
                        (map 
                            (lambda (new-row)
                                (adjoin-position
                                    new-row k rest-of-queens))  ;; 新しい列に[1..8]を追加して、みる
                        (enumerate-interval 1 board-size)   ;; [1..8]
                        )) ;; 
                    (queen-cols (- k 1))))))                ;;はじめは()
(queen-cols board-size))

(define empty-board nil)
(define (safe? row positions)
    ;(print "safe?" positions)
    (and
        (not (same-col? row positions))
        (not (slash? row positions))
    )
)

(define (adjoin-position new-row k rest-of-queens) (append (list new-row) rest-of-queens))

; 同じ列にないか
(define (same-col? row positions)
    (accumulate 
        (lambda (i acc) (or acc (= (list-ref positions (- row 1)) (list-ref positions i))))
        #f 
        (enumerate-interval 0 (- row 2))
    ))

(define (test-same-col?)
    (print "--test (same-col? 3 (list 1 3 3)) #t")
    (print (same-col? 3 (list 1 3 3)))
    (print "--test  (same-col? 3 (list 1 3 5)) #f")
    (print (same-col? 3 (list 1 3 5)))
)
(test-same-col?)

;右斜にないか
(define (slash? row positions)
    ;(print (- row 1) "," (list-ref positions (- row 1)))
    (accumulate 
        (lambda 
            (i acc) 
            ;(print i acc)
            ;(print i " " (list-ref positions i) " "
            ;        " " acc " " (+ (- row 1) (list-ref positions (- row 1))) "=" (+ i (list-ref positions i))
            ;        "," (- (- row 1) (list-ref positions (- row 1))) "=" (- i (list-ref positions i)))
            (or
                acc
                (= 
                        (+ (- row 1) (list-ref positions (- row 1))) ;; <- list-ref使うと遅い 順番にindexごと確認してくほうが早い
                        (+ i (list-ref positions i))
                )
                (= 
                        (- (- row 1) (list-ref positions (- row 1)))
                        (- i (list-ref positions i))
                )
            )
        )
    #f
    (enumerate-interval 0 (- row 2)))
)

(define (test-slash?)
    (print "--test (slash? 3 (list 3 2 1)) #t")
    (print (slash? 3 (list 3 2 1)))
    (print "--test (slash? 3 (list 5 3 1)) #f")
    (print (slash? 3 (list 5 3 1)))
    (print "--test (slash? 2 (list 1 2)) #t")
    (print (slash? 2 (list 1 2)))
    (print "--test (slash? 2 (list 1 3)) #f")
    (print (slash? 2 (list 1 3)))
    (print "--test (slash? 3 (list 2 4 3)) #t")
    (print (slash? 3 (list 2 4 3))) 
)
(test-slash?)

;--test (same-col? 3 (list 1 3 3)) #t
;#t
;--test  (same-col? 3 (list 1 3 5)) #f
;#f
;--test (slash? 3 (list 3 2 1)) #t
;#t
;--test (slash? 3 (list 5 3 1)) #f
;#f
;--test (slash? 2 (list 1 2)) #t
;#t
;--test (slash? 2 (list 1 3)) #f
;#f
;--test (slash? 3 (list 2 4 3)) #t
;#t

(define (test-nqueen?)
    (print "1-queens: " (length (queens 1)))
    (print "4-queens: " (length (queens 4)))
    (print "8-queens: " (length (queens 8)))
)
(test-nqueen?)
;1-queens: 1
;4-queens: 2
;8-queens: 92
(time (length (queens 8)))
(time (length (queens 9)))
;(time (length (queens 8)))
; real   0.777
; user   1.030
; sys    0.020
;(time (length (queens 9)))
; real  14.940
; user  21.090
; sys    0.470
;; 遅い原因はlist-ref?

Ex 2.43

新しく追加された行だけでなく、毎回全てのボードの中身をチェックしてるから。