Уча F# и създавам приложение за миночистач. Като част от това се опитвам да имам метод, който детонира всички съседни мини, ако мина бъде взривена, рекурсивно. Така че, ако имам мрежа като:
| 0 | 1 | 2 |
------------------------
0 | E-1 | M | E-2 |
1 | E-2 | E-4 | M |
2 | M | E-3 | M |
и аз детонирам мината за 0,1, то на свой ред ще детонира мината за 1,2 и това на свой ред ще детонира мината за 2,2. Мината в 2,0 няма да детонира, защото не е в съседство с никоя от другите.
На този етап всъщност внедрих полето като списък:
module CellContents =
type T =
| Empty of int
| Mine
| Crater
module Location =
type T = { Row: int; Column: int }
module Cell =
type T =
{ Content : CellContents.T
Location : Location.T }
module Field =
type T = Cell.T list
Това, с което имам проблеми, е как да започна с клетка 0,1 и да завърша със списък на всички мини, които са съседни. И така, имам нужда от списък като (показващ само координати):
let minesToDetonate = [ {Row=0;Column=1};{Row=1;Column=2};{Row=2;Column=2} ]
Нямам проблеми с получаването на съседните мини за конкретно местоположение и след това определянето на мините от тази група.
Това, с което имам проблем, е да накарам това да се рекурсира по някакъв начин и да продължи, докато не бъдат открити мини в съседство, което ми дава главен списък с мините, които трябва да взривя.
След като получа основния списък с мини, мога да ги взривя и да изградя актуализирано поле, като тези мини се превърнат в кратери.
Актуализация Отговорът на @Kevin проработи, но ми беше трудно да го разбера. В случай, че и на другите им е трудно, добавям функцията по-долу с коментари и няколко промени.
let detonateProximity (field:T) (start:Cell.T) =
let rec inner cells m acc =
match cells with
| [] -> acc
| x::xs ->
match x.Content with
|Mine ->
match proximity m.Location x.Location with
// Continue but don't accumulate
| Self -> inner xs m acc
| Near ->
// See if current cell has already been found
match acc |> List.tryFind (fun t -> t = x) with
// If it has, we're done. Pass out
// an empty list which ends recursion.
|Some _ -> []
// If it wasn't found (this parts hurts my brain)...
// calls function once for rest field and then
// using new starting point on whole field.
// Is this efficient at all?
|None -> List.concat [(inner xs m (x::acc));(inner field x (x::acc))]
// Don't accumulate, continue with rest of mines.
| Far -> inner xs m acc
// Not a mine, keep going, don't accumulate
|_ -> inner xs m acc
// The set ensures no duplicates
Set.ofList (inner field start [])
Функцията proximity
(не е показана) просто завършва логиката, която определя дали тестваната мина е референтната мина, близо до нея или далеч от нея. напр. Self
се връща, ако разстоянието между текущата клетка и мината е нула {Row=0, Column=0}.