由于不同的类型/大小,此子句无法匹配

时间:2015-08-08 12:23:27

标签: erlang

我试图在erlang中实现binary_search:

binary_search(X , List) ->
  case {is_number(x) , is_list(List)} of
  {false , false} -> {error};
  {false , true} -> {error} ;
  {true , false} -> {error} ;
  {true , true} ->
   Length = length(List) ,
   case Length of
      0 -> {false};
      1 -> case lists:member(X , List) of
              true -> {true};
              false -> {false}
           end ;
      _ ->
        Middle = (Length + 1) div 2 ,
        case X >= Middle of
            true -> binary_search(X , lists:sublist(List , Middle , Length));
            false -> binary_search(X , lists:sublist(List , 1 , Middle))
        end
   end
 end .

然而,当我尝试编译它时,我得到以下错误:“这个子句因两个行中的不同类型/大小而无法匹配”:

    {true , false} -> {error} ;
    {true , true} ->

2 个答案:

答案 0 :(得分:2)

is_number(x)将始终返回false,因为您输入了错字:x而不是X,原子而不是变量。

顺便说一句,我不知道你在经历什么,但整个代码可以写成:

binary_search(X , [_|_] = List) when is_number(X) ->
    {lists:member(X,List)};
binary_search(_,_) -> {error}.

答案 1 :(得分:2)

上下文:OP的帖子似乎是一个学习的例子 - 尝试理解Erlang中的二进制搜索 - 并在下面被视为一个(因此每次迭代内部都会调用io:format/2功能)。在制作中lists:member/2应该如Steve Vinoski在下面的评论中所述,或lists:member/2由功能负责人保护,如Pascal的答案。以下是二进制搜索的手动实现。

Pascal对于拼写错误是正确的,但这段代码有更多根本问题。让我们看看我们是否可以完全避免这种嵌套案例检查的需要,而不仅仅是找到错字。

(上面写的代码无论如何都不会起作用,因为X不应该代表索引的值,而是代表该索引保存的值,因此Middle可能永远不会匹配X。此外,还有另一个问题:你没有涵盖所有基本情况(你应该停止递归的情况)。所以下面的内部函数将它们全部作为函数头内的匹配覆盖它们,所以搜索的工作方式更为明显。请注意Middle + 1 X > Value,顺便说一下;考虑为什么这是必要的。)

关于Erlang风格的两个主要注释

首先:如果您收到错误的数据类型,只是崩溃,不要返回错误。考虑到这一点,请考虑使用警卫。

第二:如果你发现自己做了很多案例,你可以通常通过将它们命名为函数来简化你的生活。这为您提供了两个优势:

  1. 比嵌套case表达式更好的崩溃报告。
  2. 一个命名的纯函数可以测试,甚至可以很容易地进行正式验证,如果它足够小 - 这也很酷。 (作为旁注,测试的宗教有时会测试我的耐心和理智,但是当你有纯粹​​的功能时,你实际上可以至少测试你程序的那些部分 - 所以尽可能多地提炼出来尽可能这样的事情是一场大胜利。)
  3. 下面我同时做这两件事,这应该可以避免你遇到的问题,以及让你的事情更容易阅读/精神分类:

    %% Don't return errors, just crash.
    %% Only check the data on entry.
    %% Guarantee the data is sorted, as this is fundamental to binary search.
    binary_search(X, List)
            when is_number(X),
                 is_list(List) ->
        bs(X, lists:sort(List)).
    
    %% Get all of our obvious base cases out of the way as matches.
    %% Note the lack of type checking; its already been done.
    bs(_, [])   -> false;
    bs(X, [X])  -> true;
    bs(X, [_])  -> false;
    bs(X, List) ->
        ok = io:format("bs(~p, ~p)~n", [X, List]),
        Length = length(List),
        Middle = (Length + 1) div 2,
        Value = lists:nth(Middle, List),
        % This is one of those rare times I find an 'if' to be more
        % clear in meaning than a 'case'.
        if
          X == Value -> true;
          X >  Value -> bs(X, lists:sublist(List, Middle + 1, Length));
          X <  Value -> bs(X, lists:sublist(List, 1, Middle))
        end.