Знаю, что баян, но для меня было весьма показательно.
Например, вот вариант, который я написал где-то за полчаса:
-module(queens_classic). -export([solve/0]). solve() -> solve(lists:seq(1, 8), lists:seq(1, 15 + 15), 1, []). print_board([]) -> io:format("~n", []); print_board([H|T]) -> Line = [string:copies(". ", H - 1), "@ ", string:copies(". ", 8 - H)], io:format("~s~n", [Line]), print_board(T). solve(_, _, Cols, Result) when Cols > 8 -> io:format("~p~n", [Result]), print_board(Result); solve(Rows, Diags, Col, Result) -> lists:foreach(fun(Row) -> D1 = Row + Col, D2 = Row - Col + 8 + 15, T = lists:member(Row, Rows) andalso lists:member(D1, Diags) andalso lists:member(D2, Diags), if T -> Rows1 = Rows -- [Row], Diags1 = Diags -- [D1, D2], solve(Rows1, Diags1, Col + 1, [Row | Result]); true -> void end end, Rows).
Выглядит ужасно, и стиль однозначно понятно какой: C/Python на стероидах (циклы, if’ы).
А вот над этим вариантом я провозился несколько часов:
-module(queens). -export([solve/0]). solve() -> solve(1, []). print_board([]) -> io:format("~n", []); print_board([{_X, Y}|T]) -> Line = [string:copies(". ", Y - 1), "@ ", string:copies(". ", 8 - Y)], io:format("~s~n", [Line]), print_board(T). solve(X, Taken) when X > 8 -> io:format("~p~n", [Taken]), print_board(Taken); solve(X, Taken) -> L = [{X, Y} || Y <- lists:seq(1, 8), not under_attack({X, Y}, Taken)], row(L, Taken). row([], _) -> []; row([{X, Y}|L], Taken) -> solve(X + 1, [{X, Y} | Taken]), row(L, Taken). under_attack(_, []) -> false; under_attack({X, Y}, [{Xt, Yt}|L]) -> Y == Yt orelse abs(Y - Yt) == abs(X - Xt) orelse under_attack({X, Y}, L).
Вся работа со списками вручную без циклоподобных конструкций.
Печатает типа такого:
[4,7,5,2,6,1,3,8]
. . . @ . . . .
. . . . . . @ .
. . . . @ . . .
. @ . . . . . .
. . . . . @ . .
@ . . . . . . .
. . @ . . . . .
. . . . . . . @
[5,7,2,6,3,1,4,8]
. . . . @ . . .
. . . . . . @ .
. @ . . . . . .
. . . . . @ . .
. . @ . . . . .
@ . . . . . . .
. . . @ . . . .
. . . . . . . @
...
Увы, но вот эта версия мне кажется более красивой с точки зрения фукнционального стиля.
На всякий случай Makefile для обоих вариантов:
target = queens all: erlc $(target).erl erl -noshell -s $(target) solve -s init stop classic: erlc $(target)_classic.erl erl -noshell -s $(target)_classic solve -s init stop clean: -rm *.beam *.dump