Excel
VBA
関数型プログラミング

はじめに

 『Excelで関数型プログラミングに目覚めたのかもしれない』の投稿練習用の記事です。Excelで数独を解く為に作ったツールです。簡単にするために半手動です。バックトラッキング部分のロジックはありません。

関数型プログラミングといいながら、らしくないコードですが、分かりやすさ優先です。 時間があったので、より関数型プログラミングらしく書き直しました。

用意した関数

Function DI(R, I, Optional J = 1, Optional S = "")         '
    On Error Resume Next
    If I > 0 Then S = WorksheetFunction.Index(R, I, J)
    DI = S
End Function

' MM,NN ... テーブルで定義された関数の位置情報。

Function MM(R)                          ' TABLE ROW
    MM = Application.ThisCell.Row - R.Row + 1
End Function

Function NN(R)                          ' TABLE COLUMN
    NN = Application.ThisCell.Column - R.Column + 1
End Function

' CC は CONCAT互換関数です。

Function CC(ParamArray AA())
    On Error Resume Next
    Dim S: S = ""
    Dim I: For I = 0 To UBound(AA)
        S = S & AA(I)
    Next I
    CC = S
End Function

' 追加
Function IFC(C, V, Optional S = "")
    If C Then IFC = V Else IFC = S
End Function

Function RR(R, Optional M = 1, Optional N = 1, Optional H = 0, Optional W = 1, Optional S = "")  ' RANGE RESIZE
    On Error Resume Next
    H = IFC(H = 0, R.Rows.Count - M + 1, H)
    S = R.Offset(M - 1, N - 1).Resize(H, W)

    RR = S
End Function

数独を解く為の関数

Function BB(S1, Optional S = "")
    Dim I: For I = 1 To 9
        If InStr(S1, I) = 0 Then S = CC(S, I)
    Next I
    BB = S
End Function

Function BC(M)
    BC = Int((M - 1) / 3) * 3 + 1
End Function

' 追加
Function BD(R, Optional C = "", Optional S = "")   ' Rの範囲の文字列をCC
   For Each C In R
      S = CC(S, C)
   Next C
   BD = S
End Function

Function NUKU(S1, S2, Optional S3 = "")            ' S1からS2を1回抜く
    NUKU = Replace(S1, S2, S3, 1, 1)
End Function

' 追加した関数を使って書き直し
'
Function AA(R1, R2, Optional S = "")
    Dim M: M = MM(R2)
    Dim N: N = NN(R2)
    Dim K: K = DI(R1, M, N)

    If K = 0 Then
        S = BB(CC(BD(RR(R1, M, 1, 1, 9)), BD(RR(R1, 1, N, 9, 1)), BD(RR(R1, BC(M), BC(N), 3, 3))))
    ElseIf K < 10 Then
        S = K
    End If
    AA = S
End Function

Function AB(R1, R2, R3, Optional S = "")
    Dim M: M = MM(R3)
    Dim N: N = NN(R3)

    If DI(R1, M, N) = "" Then
       Dim K: K = DI(R2, M, N)

       If K < 10 Then
          S = K
       Else
          S = BB(NUKU(BD(RR(R2, BC(M), BC(N), 3, 3)), K))

          If S = "" Then S = BB(NUKU(BD(RR(R2, M, 1, 1, 9)), K))
          If S = "" Then S = BB(NUKU(BD(RR(R2, 1, N, 9, 1)), K))
       End If
    End If

    AB = S

End Function


使用方法

Excelシート上に 9x9のテーブルを3つ並べて用意します。
それぞれ左から、T1_,T2_,T3_と名前をつけます。
T1_は、入力用で、初期状態では、数独の問題を埋めていきます。
T2_は、関数 =AA(T1_,T2_) で全セルを埋めます。
T3_は、関数 =AB(T1_,T2_,T3_) で全セルを埋めます。
テーブルT3_には、一意に決まった数が表示されるので、それを
テーブルT1_に手動で転記します。簡単な問題だとそのまま解けてしまいますが、
難しい問題だと、途中で一意に決まる数字が表示されなくなります。
この場合、将棋の継盤のようにテーブル3つをコピーして手動バックトラック
を行って下さい。
テーブルT2_で、数字が2桁ある場所を見つけて、テーブルT1_にどちらかを転記し、
T3_からT1_への転記を続けます。超難問でも複数の継盤を用意すれば解けるようです。

問題をT1_に入力するとこんな感じになります。

数独.png

ではでは。