正确的实现方式(Mnesia风格/通配符/不关心)元组匹配(Erlang)

时间:2014-11-21 11:13:51

标签: erlang mnesia

我在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:我没有包含所有功能的来源,因为我认为没有包含的功能在我看来是微不足道的。如果您有兴趣,请在下面发表评论。谢谢!

2 个答案:

答案 0 :(得分:0)

我的解决方案是实现匹配的辅助函数,这使得上面提到的2 n case子句成为n orelse子句加n-1 {{ 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).

有没有更好/另类的想法?

答案 1 :(得分:0)

我会更改国际象棋棋盘表示,用{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).