オンラインSICP読書女子会 #23 (2.2.3 (3))

[オンラインSICP読書女子会 #23 (2.2.3 (3))](http://ladiespp.connpass.com/event/42175/)

ex-2.40 .. ex-2.43

## sec-2.2.3 (c) マップのネスト

> この場合、 `(list nil)` というひとつの項目からなる列、つまり空集合を生成します。
> For this, we generate `(list nil)`, which is a sequence with one item, namely the set with no elements.

「この場合、 `(list nil)` というひとつの項目、すなわち空集合、からなる列を生成します。」かな？^^;

## sec-2.2.3 (c) 実装

```scm
; [sec-2.2.3-c.scm]
;
(define (sec-2.2.3-c)
	(print "(gen-pairs 6)")
	(print ";==> " (gen-pairs 6))

	(newline)
	(print "(prime-sum-pairs 6)")
	(print ";==> " (prime-sum-pairs 6))

	(newline)
	(print "(permutations (list 1 2 3))")
	(print ";==> " (permutations (list 1 2 3)))
	#t)


; {{{ 準備.
(define accumulate fold-right)
(define nil ())

(define (enumerate-interval low high)
	(if
		(> low high)
		nil
		(cons low (enumerate-interval (+ low 1) high))))

(define (prime? n)
	(define (iter i n)
		(cond
			((> (* i i) n) #t)
			((= (remainder n i) 0) #f)
			(else (iter (+ i 1) n))))
	(if
		(< n 2)
		#f
		(iter 2 n)))
; }}} 準備.


(define (gen-pairs n)
	(accumulate
		append
		nil
		(map
			(lambda (i)
				(map
					(lambda (j) (list i j))
					(enumerate-interval 1 (- i 1))))
			(enumerate-interval 1 n))))


(define (flatmap proc seq)
	(accumulate append nil (map proc seq)))


(define (prime-sum? pair)
	(prime? (+ (car pair) (cadr pair))))


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


(define (prime-sum-pairs n)
	(map
		make-pair-sum ; (Int, Int) -> (Int, Int, Int)
		(filter
			prime-sum?
			(flatmap        ;==> [(i:Int, j:Int)]     ; concatenated.
				(lambda (i) ; i:Int -> [(i:Int, j:Int)]
					(map
						(lambda (j) (list i j)) ; j:Int -> (i:Int, j:Int)
						(enumerate-interval 1 (- i 1)))) ; [j:Int]
				(enumerate-interval 1 n))))) ; [Int]


(define (permutations s)
	(if
		(null? s)  ; 集合は空か?
		(list nil) ; 空の場合は空集合を持つ列が復帰値.
		(flatmap
			(lambda (x)
				(map
					(lambda (p) (cons x p))
					(permutations (remove x s))))
			s)))


(define (remove item sequence)
	(filter
		(lambda (x) (not (= x item)))
		sequence))
```

## sec-2.2.3 (c) 実行結果

```scm
gosh> (sec-2.2.3-c)
(gen-pairs 6)
;==> ((2 1) (3 1) (3 2) (4 1) (4 2) (4 3) (5 1) (5 2) (5 3) (5 4) (6 1) (6 2) (6 3) (6 4) (6 5))

(prime-sum-pairs 6)
;==> ((2 1 3) (3 2 5) (4 1 5) (4 3 7) (5 2 7) (6 1 7) (6 5 11))

(permutations (list 1 2 3))
;==> ((1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1))
```


## ex-2.40. unique-pairs

`1 ≤ j < i ≤ n` な `(i, j)` の組の列.

### ex-2.40 実装

```scm
; [ex-2.40.scm]]
;
(define (ex-2.40)
	(print "(unique-pairs 6)")
	(print ";==> " (unique-pairs 6))

	(newline)
	(print "(prime-sum-pairs-2.40 6)")
	(print ";==> " (prime-sum-pairs-2.40 6))
	#t)


(load "./sec-2.2.3-c") ; flatmap, prime-sum?, enumerate-interval.


(define (unique-pairs n)
	(flatmap
		(lambda (i)
			(map
				(lambda (j)
					(list i j))
				(enumerate-interval 1 (- i 1))))
		(enumerate-interval 1 n)))


(define (prime-sum-pairs-2.40 n)
	(map
		make-pair-sum
		(filter prime-sum? (unique-pairs n))))
```

### ex-2.40 実行結果

```scm
gosh> (ex-2.40)
(unique-pairs 6)
;==> ((2 1) (3 1) (3 2) (4 1) (4 2) (4 3) (5 1) (5 2) (5 3) (5 4) (6 1) (6 2) (6 3) (6 4) (6 5))

(prime-sum-pairs-2.40 6)
;==> ((2 1 3) (3 2 5) (4 1 5) (4 3 7) (5 2 7) (6 1 7) (6 5 11))
```

## ex-2.41. 特定の合計数になる3つ組

「合計で s になる」の部分若干わすれてたっ（）

### ex-2.41 実装

```scm
; [ex-2.41.scm]
;
(define (ex-2.41)
	(print "(unique-triples 5)")
	(print ";==> " (unique-triples 5))
	(print "(const-sum-pairs 10)")
	(print ";==> " (const-sum-pairs 10))
	#t)


(load "./sec-2.2.3-c") ; accumulate, flatmap, prime-sum?, enumerate-interval.


(define (const-sum-pairs s)
	(filter
		(lambda (triple) ; (i,j,k) -> Bool
			(let
				(
					(sum (+ (list-ref triple 0) (list-ref triple 1) (list-ref triple 2)))
				)
				(= sum s)))
		(unique-triples s))) ; [(i,j,k)]

(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)))
```

### ex-2.41 実行結果

```scm
gosh> (ex-2.41)
(unique-triples 5)
;==> ((3 2 1) (4 2 1) (4 3 1) (4 3 2) (5 2 1) (5 3 1) (5 3 2) (5 4 1) (5 4 2) (5 4 3))
(const-sum-pairs 10)
;==> ((5 3 2) (5 4 1) (6 3 1) (7 2 1))
```


## ex-2.42 8 Queens

ひとまず最低限動作に必要な部分を実装.
今はまだ全部が safe としておく.
つまり `(queen n)` を実行すると全部の組み合わせが解として返る.

```scm
(define empty-board nil)
(define (safe? k positions)
	#t)
(define (adjoin-position new-row k rest-of-queens)
	(cons new-row rest-of-queens))
```

queen の利き筋は斜めと縦横で, その線上にいる場合に unsafe になる.
逆に, 新しく置いた queen から見て, 斜め左上, 真上, 斜め右上のいずれかに既に queen がいる場合にのみ unsafe と判断できる.
この3方向の判定をそれぞれ `safe-left?`, `safe-column?`, `safe-right?` とおく.

```scm
(define (safe? k positions)
	(and
		(safe-left? k (car positions) (cdr positions))
		(safe-column? k (car positions) (cdr positions))
		(safe-right? (+ k 1) (car positions) (cdr positions))))

(define (safe-left? k positions)
	#t)
(define (safe-column? k positions)
	#t)
(define (safe-right? k positions)
	#t)
```

```scm
(queens 1)
;==> ((1))

(queens 2)
;==> ((1 1) (2 1) (1 2) (2 2))

(queens 3)
;==> ((1 1 1) (2 1 1) (3 1 1) (1 2 1) (2 2 1) (3 2 1) (1 3 1) (2 3 1) (3 3 1)
(1 1 2) (2 1 2) (3 1 2) (1 2 2) (2 2 2) (3 2 2) (1 3 2) (2 3 2) (3 3 2)
(1 1 3) (2 1 3) (3 1 3) (1 2 3) (2 2 3) (3 2 3) (1 3 3) (2 3 3) (3 3 3))
```

`safe-column?` を実装する.
ぐぐっと safe が減少.

```scm
(define (safe-column? k p positions)
	(cond
		((null? positions) #t)
		((= (car positions) p) #f)
		(else (safe-column? k p (cdr positions)))))
```

```scm
(queens 1)
;==> ((1))

(queens 2)
;==> ((2 1) (1 2))

(queens 3)
;==> ((3 2 1) (2 3 1) (3 1 2) (1 3 2) (2 1 3) (1 2 3))
#t
```

`safe-left?` を実装.
一段上に遡るたびに queen さんがいるかの判定する場所も1つずれていく.

```scm
(define (safe-left? k p positions)
	(cond
		((null? positions) #t)
		((= (car positions) (- p 1)) #f)
		(else (safe-left? k (- p 1) (cdr positions)))))
```

```scm
(queens 1)
;==> ((1))

(queens 2)
;==> ((1 2))

(queens 3)
;==> ((2 3 1) (3 1 2) (1 2 3))
```

`safe-right?` も実装:

```scm
(define (safe-right? k p positions)
	(cond
		((null? positions) #t)
		((= (car positions) (+ p 1)) #f)
		(else (safe-right? k (+ p 1) (cdr positions)))))
```

```scm
(queens 1)
;==> ((1))

(queens 2)
;==> ()

(queens 3)
;==> ()
```

`safe?` のなかで `k` 使ってない＼(^o^)／
盤面を右とか左に突き抜けちゃうけど計算量以外は問題ないはず？^^;

1 から 10 の解の数:

```scm
(for-each (lambda (n) (print (list n (length (queens n))))) (enumerate-interval 1 10))
;==>
(1 1)
(2 0)
(3 0)
(4 2)
(5 10)
(6 4)
(7 40)
(8 92)
(9 352)
(10 724)
```

### ex-2.42 実装

```scm
; [ex-2.42.scm]
;
(define (ex-2.42)
	(print "(queens 1)")
	(print ";==> " (queens 1))

	(newline)
	(print "(queens 2)")
	(print ";==> " (queens 2))

	(newline)
	(print "(queens 3)")
	(print ";==> " (queens 3))
	#t)


(load "./sec-2.2.3-c")


(define (queens board-size)
	(define (queen-cols k)
		(if
			(= k 0)
			(list empty-board)
			(filter
				(lambda (positions) (safe? k positions))
				(flatmap
					(lambda (rest-of-queens)
						(map
							(lambda (new-row)
								(adjoin-position
									new-row
									k
									rest-of-queens))
							(enumerate-interval 1 board-size)))
					(queen-cols (- k 1))))))

	(define empty-board nil)
	(define (safe? k positions)
		(and
			(safe-left? k (car positions) (cdr positions))
			(safe-column? k (car positions) (cdr positions))
			(safe-right? (+ k 1) (car positions) (cdr positions))))
	(define (safe-left? k p positions)
		(cond
			((null? positions) #t)
			((= (car positions) (- p 1)) #f)
			(else (safe-left? k (- p 1) (cdr positions)))))
	(define (safe-column? k p positions)
		(cond
			((null? positions) #t)
			((= (car positions) p) #f)
			(else (safe-column? k p (cdr positions)))))
	(define (safe-right? k p positions)
		(cond
			((null? positions) #t)
			((= (car positions) (+ p 1)) #f)
			(else (safe-right? k (+ p 1) (cdr positions)))))
	(define (adjoin-position new-row k rest-of-queens)
		(cons new-row rest-of-queens))
	(queen-cols board-size))
```

### ex-2.42 実行結果

```scm
gosh> (ex-2.42)
(queens 1)
;==> ((1))

(queens 2)
;==> ()

(queens 3)
;==> ()
```

## ex-2.43

本来は board-size 回, 自分に必要な盤面を1回だけ呼べばよかったのが,
board-size 回, さらに board-size 回再計算してしまっているため.

O(n) が O(n^2) になっていることから,
T^2 の時間が必要になると見積もれる.