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

Erlang 由于类型/大小不同,此子句无法匹配,erlang,Erlang,我尝试在erlang中实现二进制搜索: 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 Len

我尝试在erlang中实现二进制搜索:

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} ->
is_number(x)将始终返回false,因为您输入了一个错误:x而不是x,原子而不是变量

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

binary_search(X , [_|_] = List) when is_number(X) ->
    {lists:member(X,List)};
binary_search(_,_) -> {error}.
is_number(x)将始终返回false,因为您输入了一个错误:x而不是x,原子而不是变量

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

binary_search(X , [_|_] = List) when is_number(X) ->
    {lists:member(X,List)};
binary_search(_,_) -> {error}.
上下文:OP的帖子似乎是一个学习的例子——试图理解Erlang中的二进制搜索——并被视为下面的例子(因此调用
io:format/2
内部函数的每次迭代)。在生产过程中,如Steve Vinoski在下面的评论中所述,应使用
list:member/2
,或如Pascal的回答中所述,使用由职能负责人保护的
list:member/2
。下面是二进制搜索的手动实现

Pascal关于打字错误的说法是正确的,但是这段代码有更基本的问题。与其只是查找输入错误,不如让我们看看是否可以完全避免这种嵌套的案例检查

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

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

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

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

  • 一个比嵌套的
    case
    表达式更好的崩溃报告
  • 如果一个命名的纯函数足够小,那么它可以很容易地进行测试甚至正式验证,这也是非常酷的。(顺便说一句,测试的宗教信仰有时会考验我的耐心和理智,但当你拥有纯函数时,你实际上至少可以测试程序的那些部分——因此尽可能多地提取这类东西是一个巨大的胜利。)
  • 下面我两者都做了,这应该可以避免您遇到的问题,并且使事情更容易阅读/整理:

    %% 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.
    
    %%不返回错误,只是崩溃。
    %%只检查输入的数据。
    %%确保数据已排序,因为这是二进制搜索的基础。
    二进制搜索(X,列表)
    _数(X)是什么时候,
    是列表(列表)->
    bs(X,列表:排序(列表))。
    %%把我们所有明显的基本情况都作为匹配项处理掉。
    %%注意缺乏类型检查;它已经完成了。
    bs(u,[])->假;
    bs(X,[X])->正确;
    bs(X,[])->假;
    bs(X,列表)->
    ok=io:format(“bs(~p,~p)~n”,[X,List]),
    长度=长度(列表),
    中间=(长度+1)第2部分,
    值=列表:第n个(中间,列表),
    %这是我难得发现“如果”更重要的一次
    %在意义上比“案例”更清楚。
    如果
    X==值->真;
    X>Value->bs(X,列表:子列表(列表,中间+1,长度));
    Xbs(X,列表:子列表(列表,1,中间))
    结束。
    
    上下文:OP的帖子似乎是一个学习示例——试图理解Erlang中的二进制搜索——并被视为下面的示例(因此调用
    io:format/2
    内部函数的每次迭代)。在生产过程中,如Steve Vinoski在下面的评论中所述,应使用
    list:member/2
    ,或如Pascal的回答中所述,使用由职能负责人保护的
    list:member/2
    。下面是二进制搜索的手动实现

    Pascal关于打字错误的说法是正确的,但是这段代码有更基本的问题。与其只是查找输入错误,不如让我们看看是否可以完全避免这种嵌套的案例检查

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

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

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

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

  • 一个比嵌套的
    case
    表达式更好的崩溃报告
  • 如果一个命名的纯函数足够小,那么它可以很容易地进行测试甚至正式验证,这也是非常酷的。(顺便说一句,测试的宗教信仰有时会考验我的耐心和理智,但当你拥有纯函数时,你实际上至少可以测试程序的那些部分——因此尽可能多地提取这类东西是一个巨大的胜利。)
  • 下面我两者都做了,这应该可以避免您遇到的问题,并且使事情更容易阅读/整理:

    %% 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.
    
    %%D