Правильный способ реализации сопоставления кортежей (в стиле Mnesia / подстановочных знаков / безразлично) (Erlang)

Я реализую шахматную игру (китайские шахматы, а точнее, Xiangqi) в Erlang.

Часть представлена ​​кортежем {Color, Type}, а точка (т. Е. Местоположение) представлена ​​кортежем {File, Rank}. Доска представлена ​​схемой "точка-часть" (т.е. #{point() => piece()}).

Есть функция для запроса, занята ли конкретная точка на доске фигурой или нет:

is_point_occupied_simple(Board, Point) ->
    ensure_is_point(Point),
    case maps:find(Point, Board) of
        {ok, _} ->
            true;
        error ->
            false
    end.

Однако я хотел бы добавить необязательный параметр для проверки цвета детали - если точка занята куском указанного цвета, функция возвращает true; в противном случае возвращается false. Если меня не волнует цвет детали, я могу просто указать '_' в параметре TargetColor (или, что то же самое, вызвать is_point_occupied/2):

is_point_occupied(Board, Point) ->
    is_point_occupied(Board, Point, '_').

is_point_occupied(Board, Point, '_') ->
    ensure_is_point(Point),
    case maps:find(Point, Board) of
        {ok, _} ->
            true;
        error ->
            false
    end;

is_point_occupied(Board, Point, TargetColor) ->
    ensure_is_point(Point),
    ensure_is_color(TargetColor),
    case maps:find(Point, Board) of
        {ok, {TargetColor, _}} ->
            true;
        {ok, _} ->
            false;
        error ->
            false
    end.

Мне не нравится приведенная выше реализация из-за большой доли копирования и вставки, поэтому я упростил указанную выше функцию следующим образом:

is_point_occupied_2(Board, Point) ->
    is_point_occupied_2(Board, Point, '_').

is_point_occupied_2(Board, Point, TargetColor) ->
    ensure_is_point(Point),
    ensure_is_color_or_wildcard(TargetColor),
    case maps:find(Point, Board) of
        {ok, {TargetColor, _}} ->
            true;
        {ok, _} ->
            is_wildcard(TargetColor);
        error ->
            false
    end.

Функция is_wildcard/1 - это просто однострочник:

is_wildcard(Wildcard) -> Wildcard =:= '_'.

Теперь я хотел бы пойти дальше и заменить TargetColor на TargetPiece, который является {TargetColor, TargetType} кортежем. Нет, один или оба элемента кортежа могут быть подстановочным знаком ('_'). Мне было трудно написать case пункты. Я также заметил, что для соответствия n-кортежу, разрешающему «безразлично» таким образом, необходимы 2 предложения n. Очевидно, что это неправильный способ реализовать это.

Есть ли у кого-нибудь идеи получше?

PS: Я не включил исходный код всех функций, поскольку те, которые я не включил, на мой взгляд, тривиально реализовать. Пожалуйста, оставьте комментарий ниже, если вам интересно. Спасибо!


person Siu Ching Pong -Asuka Kenji-    schedule 21.11.2014    source источник


Ответы (2)


Мое решение этой проблемы - реализовать вспомогательную функцию для сопоставления, которая превращает 2 предложения n case, упомянутых выше, в предложения n orelse плюс предложения n-1 andalso:

match_piece({Color, Type} = Piece, {TargetColor, TargetType} = TargetPiece) ->
    ensure_is_piece(Piece),
    ensure_is_piece_or_wildcard(TargetPiece),
    (is_wildcard(TargetColor) orelse Color =:= TargetColor)
        andalso (is_wildcard(TargetType) orelse Type =:= TargetType).

Основная функция практически не меняется:

is_point_occupied_3(Board, Point) ->
    is_point_occupied_3(Board, Point, {'_', '_'}).

is_point_occupied_3(Board, Point, TargetPiece) ->
    ensure_is_point(Point),
    ensure_is_piece_or_wildcard(TargetPiece),
    case maps:find(Point, Board) of
        {ok, Piece} ->
            match_piece(Piece, TargetPiece);
        error ->
            false
    end.

Есть ли лучшие / альтернативные идеи?

person Siu Ching Pong -Asuka Kenji-    schedule 21.11.2014

Я бы изменил представление шахматной доски, заполнив всю доску либо фигурой {Color, Type}, либо никакой фигурой {none, none}, чтобы ваш код мог быть более регулярным:

-module (testmatch).

-compile([export_all]).

is_point_occupied(Board,Point) -> not match(maps:find(Point, Board),{none,none}).
is_point_occupied(Board,Point,Color) -> match(maps:find(Point, Board),{Color,'_'}).
is_point_occupied(Board,Point,Color,Type) -> match(maps:find(Point, Board),{Color,Type}).

match({ok,{C,T}},{C,T}) -> true;
match({ok,{_C,T}},{'_',T}) -> true;
match({ok,{C,_T}},{C,'_'}) -> true;
match(_,_) -> false.

test() ->
    Board = #{1 =>{none,none}, 2 =>{black,king}, 3 => {white,tower} },
    false = is_point_occupied(Board,1),
    true = is_point_occupied(Board,2),
    false = is_point_occupied(Board,2,red),
    true = is_point_occupied(Board,2,black),
    false = is_point_occupied(Board,3,'_',king),
    true = is_point_occupied(Board,3,'_',tower),
    true = is_point_occupied(Board,2,black,king),
    false = is_point_occupied(Board,2,black,tower),
    false = is_point_occupied(Board,2,white,king).
person Pascal    schedule 22.11.2014
comment
Спасибо за ваш ответ! Итак, означает ли это, что вашей доске требуется 8 x 8 = 64 элемента для шахмат (и 9 x 10 = 90 элементов для китайских шахмат)? Поскольку здесь важно потребление памяти, я думаю, что это не лучший подход. Кроме того, для сопоставления n-кортежа {E1, E2, ..., En} необходимо записать 2 ^ n предложений match / 2. Как я уже сказал, я сомневаюсь, что это правильный способ сделать это. Кстати, шахматы здесь используются только в качестве примера, мое основное внимание уделяется сопоставлению стилей безразличия. Обязательно ли менять определение доски? Есть ли для этого более элегантное решение, чем мое? Спасибо! - person Siu Ching Pong -Asuka Kenji-; 22.11.2014