## common lispを紹介するプログラムの一例
#### nz_tcoder
---
## 目次
* はじめに
* 数独リゾルバ
* 比較してみる
* まとめ
---
## はじめに
lispはマイナーです。
* 原因は? もっと広めるには?
* 噂は聞いていても、実際にプログラムを見る/触ることがない?
* 記号処理、人工知能などのキーワードが敷居を高くしているかも?
* リスト処理は何がうれしいか分からない?
他の言語で書かれたプログラムをlispで書くことで、lispの良さを伝えられないか。
---
## 数独リゾルバ
プログラム言語Ruby[1]のイントロダクション
> コメントと空行を除くと、ちょうど129行のコードが残る。
>
> ...(中略)...
>
> このサンプルは、Rubyのパワーと表現力をよく示していると思うがどうだろうか。
ではcommon lispで書くとどうなる?
パワーと表現力は?
---
## 数独リゾルバ(概要)
* possible: セル(row,col)について、配置可能な数字を求める。

---
## 数独リゾルバ(概要)
* scan:
1. 未設定の各セルに対しpossibleを呼ぶ(イテレータ:each_unknown利用)
1. possibleの結果(配置可能な数字)が一つであれば、その値をセルに設定する。
1. セルの設定ができなくなったら終了する。終了時には、possibleの結果が最も少ないセルのrow、col、配置可能な数字のリストを返す。
* resolve:
1. scanを呼び出す。
1. 数独が解けていなければ、配置可能な数字を順番に試す(再帰的にresolveを呼ぶ)
---
## 実行例(ruby:easy)
```
$ tail -5 sudoku.rb
raise Impossible
end
end
puts Sudoku.solve(Sudoku::Puzzle.new(ARGF.readlines)) # 一行追加
$ sed '/^ *$/d' sudoku.rb |sed '/^ *#/d' |wc
127 653 4977
```
ー行追加して127行?
```
$cat easy.txt
.5...4..7
8..72...1
..6.8..3.
9.8..24.6
1...473..
423659...
58.216793
.1759...4
6924.3815
$ ruby sudoku.rb < easy.txt
251364987
839725641
746981532
978132456
165847329
423659178
584216793
317598264
692473815
```
--
## 実行例(ruby:normal)
```
$ cat normal.txt
.....7...
9....4...
..6.....1
.5..1....
7.....48.
....6..2.
..1.5...2
48.......
.........
$ ruby sudoku.rb < normal.txt
138627594
975134268
246985371
652418937
713592486
894763125
361859742
489271653
527346819
```
---
## common lisp版の方針
* オブジェクト指向(CLOS)は使わない。
* common lisp版イテレータの実装はしない。
* 数独の表現には多次元配列(Array)を用いる。
* (aref *配列* *i* *j* ...)
多次元でもOK
* (row-major-aref *配列* *i*)
*i* は row-major order
プログラムは https://github.com/nz-tcoder/kansai-lisp-2/tree/master/lisp
--
### row-major order


---
## 実行例(lisp: easy)
```
$ sed '/^ *$/d' sudoku.lisp | sed '/^ *;/d' |wc
87 395 3660
CL-USER> easy
((0 5 0 0 0 4 0 0 7) (8 0 0 7 2 0 0 0 1) (0 0 6 0 8 0 0 3 0)
(9 0 8 0 0 2 4 0 6) (1 0 0 0 4 7 3 0 0) (4 2 3 6 5 9 0 0 0)
(5 8 0 2 1 6 7 9 3) (0 1 7 5 9 0 0 0 4) (6 9 2 4 0 3 8 1 5))
CL-USER> (sudoku easy)
#2A((2 5 1 3 6 4 9 8 7)
(8 3 9 7 2 5 6 4 1)
(7 4 6 9 8 1 5 3 2)
(9 7 8 1 3 2 4 5 6)
(1 6 5 8 4 7 3 2 9)
(4 2 3 6 5 9 1 7 8)
(5 8 4 2 1 6 7 9 3)
(3 1 7 5 9 8 2 6 4)
(6 9 2 4 7 3 8 1 5))
```
--
## 実行例(lisp: normal)
```
CL-USER> normal
((0 0 0 0 0 7 0 0 0) (9 0 0 0 0 4 0 0 0) (0 0 6 0 0 0 0 0 1)
(0 5 0 0 1 0 0 0 0) (7 0 0 0 0 0 4 8 0) (0 0 0 0 6 0 0 2 0)
(0 0 1 0 5 0 0 0 2) (4 8 0 0 0 0 0 0 0) (0 0 0 0 0 0 0 0 0))
CL-USER> (sudoku normal)
#2A((1 3 8 6 2 7 5 9 4)
(9 7 5 1 3 4 2 6 8)
(2 4 6 9 8 5 3 7 1)
(6 5 2 4 1 8 9 3 7)
(7 1 3 5 9 2 4 8 6)
(8 9 4 7 6 3 1 2 5)
(3 6 1 8 5 9 7 4 2)
(4 8 9 2 7 1 6 5 3)
(5 2 7 3 4 6 8 1 9))
```
---
## 比較してみる(0)
#### each_unknown(ruby)
```ruby
BoxOfIndex = [
0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,
3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,
6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8
].freeze
def each_unknown
0.upto 8 do |row|
0.upto 8 do |col|
index = row*9+col
next if @grid[index] != 0
box = BoxOfIndex[index]
yield row, col, box
end
end
end
```
---
## 比較してみる(0)
#### unknown_cells(common lisp)
```lisp
(defconstant +box-index-list+
(loop for n in '(0 3 6)
append (loop repeat 3
append (loop repeat 3
for i from n
append (loop repeat 3 collect i)))))
(defun box-index-of (idx)
(nth idx +box-index-list+))
(defun unknown-cells (pz)
(loop repeat (array-total-size pz)
for idx from 0
for cell = (row-major-aref pz idx)
if (= cell 0) collect (list (floor idx 9) (mod idx 9) (box-index-of idx))))
```
---
## 比較してみる(1)
#### possible(ruby)
```ruby
def possible(row, col, box)
AllDigits - (rowdigits(row) + coldigits(col) + boxdigits(box))
end
def rowdigits(row)
@grid[row*9,9] - [0]
end
def coldigits(col)
result = [] # Start with an empty array
col.step(80, 9) {|i| # Loop from col by nines up to 80
v = @grid[i] # Get value of cell at that index
result << v if (v != 0) # Add it to the array if non-zero
}
result # Return the array
end
BoxToIndex = [0, 3, 6, 27, 30, 33, 54, 57, 60].freeze
def boxdigits(b)
i = BoxToIndex[b]
[
@grid[i], @grid[i+1], @grid[i+2],
@grid[i+9], @grid[i+10], @grid[i+11],
@grid[i+18], @grid[i+19], @grid[i+20]
] - [0]
end
```
---
## 比較してみる(1)
#### possible(common lisp)
``` lisp
(defun possible (pz row column box)
(set-difference +all-digits+
(remove-duplicates (append (row-digits pz row)
(column-digits pz column)
(box-digits pz box)))))
(defun row-digits (pz row)
(remove-zero (mapcar #'(lambda (x) (aref pz row x))
'(0 1 2 3 4 5 6 7 8))))
(defun column-digits (pz column)
(remove-zero (mapcar #'(lambda (x) (aref pz x column))
'(0 1 2 3 4 5 6 7 8))))
(defconstant +box-index-list+
(loop for n in '(0 3 6)
append (loop repeat 3
append (loop repeat 3
for i from n
append (loop repeat 3 collect i)))))
(defun box-digits (pz box)
(remove-zero (mapcar #'(lambda (x) (row-major-aref pz x))
(loop for digit in +box-index-list+
for row-major-index from 0
if (= digit box) collect row-major-index))))
```
---
## 比較してみる(2)
#### scan(ruby)
``` ruby
def Sudoku.scan(puzzle)
unchanged = false # This is our loop variable
until unchanged
unchanged = true
rmin,cmin,pmin = nil
min = 10
puzzle.each_unknown do |row, col, box|
p = puzzle.possible(row, col, box)
case p.size
when 0
raise Impossible
when 1
puzzle[row,col] = p[0]
unchanged = false
else
if unchanged && p.size < min
min = p.size
rmin, cmin, pmin = row, col, p
end
end
end
end
return rmin, cmin, pmin
end
```
---
## 比較してみる(2)
#### scan(common lisp)
``` lisp
(defun scan (pz)
(loop named outer for cells = (unknown-places pz)
do
(loop for (r c b) in cells
for digit-list = (possible pz r c b)
if (null digit-list) do
(signal 'impossible)
else if (= (length digit-list) 1) do
(setf (aref pz r c) (car digit-list))
and count r into update
else minimize (length digit-list) into min
and collect (list r c digit-list) into not-determined
finally
(if (zerop update)
(return-from outer
(values (find min not-determined
:key #'(lambda (x) (length (third x))))
(= min 0)))))))
```
---
## 比較してみる(3)
#### solve(ruby)
``` ruby
def Sudoku.solve(puzzle)
puzzle = puzzle.dup
r,c,p = scan(puzzle)
return puzzle if r == nil
p.each do |guess|
puzzle[r,c] = guess
begin
return solve(puzzle)
rescue Impossible
next
end
end
raise Impossible
end
```
---
## 比較してみる(3)
#### solve(common lisp)
```lisp
(defun solve (puzzle)
(let ((pz (copy-array puzzle)))
(multiple-value-bind (possible finish) (scan pz)
(if finish
pz
(destructuring-bind (r c guess-list) possible
(loop for guess in guess-list
do
(setf (aref pz r c) guess)
(handler-case
(return (solve pz))
(impossible ()))
finally
(signal 'impossible)))))))
```
---
## まとめ
* 87行プログラム
* 手を抜いた部分もあったかもしれない
* lispのインデント
* '-'を変数名、関数名に使えるのは楽
* 他にない機能(destructuring-bind,loop,condition(cltl2),multiple-value-bindなど)を紹介できた
--
### scanの末尾を比較
ruby
```
if unchanged && p.size < min
min = p.size
rmin, cmin, pmin = row, col, p
end
end
end
end
return rmin, cmin, pmin
end
```
common lisp
```
(if (zerop update)
(return-from outer
(values (find min not-determined
:key #'(lambda (x) (length (third x))))
(= min 0)))))))
```
--
### possbileでの比較
ruby
```
def possible(row, col, box)
AllDigits - (rowdigits(row) + coldigits(col) + boxdigits(box))
end
```
common lisp
```
(defun possible (pz row column box)
(set-difference +all-digits+
(remove-duplicates (append (row-digits pz row)
(column-digits pz column)
(box-digits pz box)))))
```
---
## 参考文献
1. プログラミング言語Ruby(David Flanagan,まつもとゆきひろ共著,オライリージャパン,
Example code(http://examples.oreilly.com/9780596516178/RPLExamples.tar.gz))
1. Common Lisp Recipes(Edumnd Weitz著,Apress)
1. 実践Common Lisp(Peter Seibel著,オーム社)
(英語版 http://www.gigamonkeys.com/book/)
1. 数独問題集(http://www.sudokugame.org/)