【问题标题】:Proper way to implement (Mnesia-style / wildcard / don't care) tuple matching (Erlang)正确的实现方式(Mnesia-style / wildcard / don't care)元组匹配(Erlang)
【发布时间】:2015-01-19 12:29:46
【问题描述】:

我正在用 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 元组,需要 2n 子句。所以显然这不是实现这一点的正确方法。

有人有更好的想法吗?

PS:我没有包含所有函数的来源,因为我认为我没有包含的那些实现起来很简单。如果您有兴趣,请在下方留言。谢谢!

【问题讨论】:

    标签: erlang mnesia


    【解决方案1】:

    我对此的解决方案是实现一个辅助函数进行匹配,将上面提到的2ncase子句变成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).
    

    main函数变化不大:

    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.
    

    有没有更好/替代的想法?

    【讨论】:

      【解决方案2】:

      我会改变棋盘的表示,用一块 {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).
      

      【讨论】:

      • 感谢您的回答!那么,这是否意味着您的棋盘需要 8 x 8 = 64 个国际象棋元素(中国象棋需要 9 x 10 = 90 个元素)?由于内存消耗在这里很重要,我认为这可能不是一个好方法。此外,要匹配一个 n 元组 {E1, E2, ..., En},必须编写 2^n match/2 个子句。正如我所提到的,我怀疑这是否是正确的方法。顺便说一句,国际象棋在这里只是作为一个例子,我主要关注的是“不关心”风格的匹配。是否必须更改董事会定义?有没有比我的更优雅的解决方案来实现这一目标?谢谢!
      猜你喜欢
      • 1970-01-01
      • 2017-11-10
      • 2010-11-12
      • 2015-05-06
      • 1970-01-01
      • 2015-05-29
      • 2016-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多