Data.Array で数独のテーブルを表現してみる

配列の練習にData.Array で数独のテーブルを表現してみます。

import Data.Array

data Cell = Cell{digit::Int, mark::Char} deriving(Eq,Show)

blockSize :: ((Int, Int), (Int, Int))
blockSize = ((1,1),(3,3))

type Block = Array (Int, Int) Cell

-- 3 x 3 のセルを持つブロック
block :: Block
block = array blockSize $ zip (range blockSize)  (repeat (Cell 0 '*'))
{-
Main> block 
-- >
array ((1,1),(3,3)) [((1,1),Cell {digit = 0, mark = '*'}),((1,2),Cell {digit = 0, mark = '*'}),((1,3),Cell {digit = 0, mark = '*'}),
                     ((2,1),Cell {digit = 0, mark = '*'}),((2,2),Cell {digit = 0, mark = '*'}),((2,3),Cell {digit = 0, mark = '*'}),
                     ((3,1),Cell {digit = 0, mark = '*'}),((3,2),Cell {digit = 0, mark = '*'}),((3,3),Cell {digit = 0, mark = '*'})]
-}
type Sudoku = Array (Int, Int) (Array (Int, Int) Cell)

-- 3 x 3 のブロックを持つ数独テーブル
sudoku :: Array (Int, Int) (Array (Int, Int) Cell)
sudoku = array blockSize $ zip (range blockSize)  (repeat block)
{-
Main> sudoku
-- >
array ((1,1),(3,3)) [
  ((1,1),array ((1,1),(3,3)) [((1,1),Cell {digit = 0, mark = '*'}),((1,2),Cell {digit = 0, mark = '*'}),((1,3),Cell {digit = 0, mark = '*'}),
                              ((2,1),Cell {digit = 0, mark = '*'}),((2,2),Cell {digit = 0, mark = '*'}),((2,3),Cell {digit = 0, mark = '*'}),
                              ((3,1),Cell {digit = 0, mark = '*'}),((3,2),Cell {digit = 0, mark = '*'}),((3,3),Cell {digit = 0, mark = '*'})]),
  ((1,2),array ((1,1),(3,3)) [((1,1),Cell {digit = 0, mark = '*'}),((1,2),Cell {digit = 0, mark = '*'}),((1,3),Cell {digit = 0, mark = '*'}),
                              ((2,1),Cell {digit = 0, mark = '*'}),((2,2),Cell {digit = 0, mark = '*'}),((2,3),Cell {digit = 0, mark = '*'}),
                              ((3,1),Cell {digit = 0, mark = '*'}),((3,2),Cell {digit = 0, mark = '*'}),((3,3),Cell {digit = 0, mark = '*'})]), 
  ((1,3),array ((1,1),(3,3)) [((1,1),Cell {digit = 0, mark = '*'}),((1,2),Cell {digit = 0, mark = '*'}),((1,3),Cell {digit = 0, mark = '*'}),
                              ((2,1),Cell {digit = 0, mark = '*'}),((2,2),Cell {digit = 0, mark = '*'}),((2,3),Cell {digit = 0, mark = '*'}),
                              ((3,1),Cell {digit = 0, mark = '*'}),((3,2),Cell {digit = 0, mark = '*'}),((3,3),Cell {digit = 0, mark = '*'})]),
  ((2,1),array ((1,1),(3,3)) [略]),
  ((2,2),array ((1,1),(3,3)) [略]),
  ((2,3),array ((1,1),(3,3)) [略]),
  ((3,1),array ((1,1),(3,3)) [略]),
  ((3,2),array ((1,1),(3,3)) [略]),
  ((3,3),array ((1,1),(3,3)) [略])]

-}

-- ! で配列要素の取得です。
--  // で配列要素の書き換えですが、要素を書き換えるのではなく、
--     新しい配列が作られます。
changeCell :: Sudoku -> (Int, Int) -> (Int, Int) -> Int -> Char -> Sudoku
changeCell table pBlock pCell newNum newMark = table // [(pBlock,((sudoku ! pBlock) // [(pCell,Cell{digit = newNum, mark=newMark})]) )]
{-
-- 左上の隅を書き換えてみる。
Main> changeCell sudoku (1,1) (1,1) 999 'x' 
-- >
array ((1,1),(3,3)) [((1,1),array ((1,1),(3,3)) [((1,1),Cell {digit = 999, mark = 'x'})
                     ・・・略・・・
                     ]
-- 右下の隅を書き換えてみる。
Main> changeCell sudoku (3,3) (3,3) 999 'x' 
-- >
array ((1,1),(3,3)) [
                     ・・・略・・・
                     ((3,3),array ((1,1),(3,3)) [
                                                 ・・・略・・・
                                                 ((3,3),Cell {digit = 999, mark = 'x'})])]