見出し画像

再帰呼出に挑戦 「Python 18 lines: 8-Queens Problem」より

先日、Python の 18行プログラム「8-Queens Problem」を紹介しました。

この記事の最後に次のような問題を2つ提示しました。

(1)関数「under_attack」を再帰呼出で書けるでしょうか。

(2)関数「solve」を再帰呼出を使わずに書けるでしょうか。

そして今回は回答編です。


関数「under_attack」を再帰呼出で書いてみた

BOARD_SIZE = 8

def under_attack(left, col, right, queens, n):
    if n == 0:
        return False
    
    if n <= len(queens):
        r, c = queens[n-1]
        if c in (left, col, right):
            return True
        
    return under_attack(left-1, col, right+1, queens, n-1)

def solve(n):
    if n == 0:
        return [[]]

    smaller_solutions = solve(n - 1)

    return [solution+[(n,i+1)]
        for i in range(BOARD_SIZE)
            for solution in smaller_solutions
                if not under_attack(i+1, i+1, i+1, solution, n)]

ちょっと微妙な感じ。
引数が増えてしまった。
left、right への破壊的な代入はなくなったけど。

「if n <= len(queens):」
この条件文をなくせないものか。

「エレガント」というには一歩足りないような。


関数「solve」を再帰呼出を使わずに書いてみた

BOARD_SIZE = 8

def under_attack(col, queens):
    left = right = col

    for r, c in reversed(queens):
        left, right = left - 1, right + 1

        if c in (left, col, right):
            return True
    return False

def solve():
    new_solutions = [[]]
    
    for n in range(BOARD_SIZE):
        smaller_solutions = new_solutions
        new_solutions = []
        
        for i in range(BOARD_SIZE):
            for solution in smaller_solutions:
                if not under_attack(i+1, solution):
                    new_solutions += [solution + [(n+1,i+1)]]
    return new_solutions

なんとなく。
ちょっとややこしいですね。

「for n in range(BOARD_SIZE)」でループするのですが、前のループの結果である「new_solutions」を使わなければなりません。

1.smaller_solutions に待避してから
2.new_solutions をクリアして
3.新しい new_solutions を作り出す

という手順なります。

こんな風に書いてみると、データがプログラミングの要であるとつくづく思います。過去に出会った「理解が難しいコード」に間違いなくあったのは「よくわからないデータ」でした。

わかりやすいコードを目指して。


この記事が気に入ったらサポートをしてみませんか?