Алгоритм возврата в F#: как работает неизменность?

Хорошо, поэтому я пытаюсь написать алгоритм обратного отслеживания, который может принимать входные данные, например:

0 2 3 1 (top-right location, length, horizontal or vertical)
1 0 4 0
2 2 4 0
1 3 3 1
top (the actual words)
that
toga
cat

И выплюнуть кроссворд, типа:

**c***
that**
**toga
***p**

Код, который у меня есть до сих пор:

//prints the puzzle
let printPuzzle (puzzle : char[,]) =
    printfn "%s" ""
    printfn "%A" puzzle
    printfn "%s" ""

//checks if the words fits
let rec doesItFit place (puzzle : char[,]) (word : seq<char>) =
    let (row, col, length, isVertical) = place
    if length <> (Seq.length word) then
        (puzzle, false)
    else
        match (Seq.toList word) with
        | [] -> (puzzle, true)
        | letter::rest ->
            printfn "%c" letter
            printPuzzle puzzle
            if isVertical = 0 then
                if puzzle.[row, col] = '*' || puzzle.[row, col] = letter then
                    puzzle.[row, col] <- letter
                    doesItFit (row, col+1, length-1, isVertical) puzzle rest 
                else
                    (puzzle, false)
            else
                if puzzle.[row, col] = '*' || puzzle.[row, col] = letter then
                    puzzle.[row, col] <- letter
                    doesItFit (row+1, col, length-1, isVertical) puzzle rest   
                else
                    (puzzle, false)

//the actual backtracking algorithm... goes through all places and all words
//trying to make stuff fit
let rec sort words places (puzzle : char[,]) =
    match places with
    | [] -> (puzzle, true)
    | place::rest ->
        let rec checkWords words place puzzle =
            match words with
            | [] ->
                printfn "%s" "failure, backtracking" 
                puzzle, false
            | word::otherWords ->
                let attempt = doesItFit place puzzle word
                if snd attempt then
                    printfn "%s" "success, entering if block"
                    let nextLevel = sort words rest (fst attempt)
                    if (snd nextLevel) then
                        nextLevel
                    else
                        checkWords otherWords place puzzle
                else
                    checkWords otherWords place puzzle
        checkWords words place puzzle

//line for testing
printPuzzle (fst (sort ["cat"; "that"; "toga"; "top"] [(0, 2, 3, 1); (1, 0, 4, 0); (2, 2, 4, 0); (1, 3, 3, 1)] (Array2D.create 6 6 '*')));;

Вот результат запуска тестовой строки:

c

[['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

a

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['*'; '*'; 'a'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

success, entering if block
t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['*'; '*'; 'a'; '*'; '*'; '*']
 ['*'; '*'; 't'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

h

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; '*'; 'a'; '*'; '*'; '*']
 ['*'; '*'; 't'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

a

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; '*'; '*'; '*']
 ['*'; '*'; 't'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; '*'; '*'; '*']
 ['*'; '*'; 't'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

success, entering if block
t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

h

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

a

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

success, entering if block
t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; 't']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

o

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; 't']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

failure, backtracking
t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; 't']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

o

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; 't']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

failure, backtracking
t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; 't']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

o

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; 't']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

failure, backtracking
t

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; 't']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

failure, backtracking

[['*'; '*'; 'c'; '*'; '*'; '*']
 ['t'; 'h'; 'a'; 't'; '*'; '*']
 ['*'; '*'; 't'; 'h'; 'a'; 't']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']
 ['*'; '*'; '*'; '*'; '*'; '*']]

Я думаю, моя проблема в том, что я не уверен, как работает неизменяемость в F#. Судя по тому, что делает моя программа, когда головоломка передается после неудачной попытки, головоломка была изменена. Для меня это не имеет особого смысла, так как я думал, что F# не позволит его модифицировать. Я хотел бы объяснить, почему этот код использует модифицированную головоломку при проверке слова после возврата, а не исходную, немодифицированную головоломку.


person MirroredFate    schedule 06.05.2012    source источник


Ответы (1)


Ваш код использует изменяемый массив char[,] для представления головоломки, поэтому, изменяя его в функции doesItFit (используя оператор <-), вы фактически изменяете состояние объекта. Когда код возвращается из функции doesItFit, массив изменился.

По сути есть три пути решения проблемы:

  • Используйте неизменяемые типы для представления головоломок — если вы используете список F# (список списков) для представления головоломок, вы не можете изменить его (поскольку списки неизменяемы) и поэтому вы не попадете в эту ситуацию.

  • Клонируйте объекты по мере необходимости — вам не нужно использовать неизменяемые типы для написания кода в функциональном стиле. Просто клонируйте массив, прежде чем передать его функции, которая может его изменить. Вы можете использовать Array.copy для этого.

  • Восстановить состояние перед возвратом — когда функция дает сбой, она должна отменить все изменения
    (это не чисто функциональный подход, но он может быть более эффективным — думайте об этом как об оптимизации)

person Tomas Petricek    schedule 06.05.2012
comment
Спасибо. Думаю, если бы я повнимательнее прочитал об Array2D, я бы понял это. Я ценю вашу помощь! - person MirroredFate; 07.05.2012