【问题标题】:How to case switch in erlang using a function value?如何使用函数值在erlang中切换大小写?
【发布时间】:2020-09-28 00:06:02
【问题描述】:

尽管看起来可以运行,但该程序正在崩溃。我不明白为什么。我正在尝试完成深度合并并需要条件逻辑。

给定以下列表:

    ManOne = #{ "Bob" => #{"Sagget" => #{}} }
    ManTwo = #{ "Bob" => #{"Daniels" => #{}} }

我正在尝试将它们进行如下比较,此函数按预期返回 true:

check_if_same(M1, M2) -> 
   {ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.
merger(M1, M2) ->
   M1_Keys = maps:keys(M1),
   M2_Keys = maps:keys(M2),
   do_merge(M1, M2, M1_Keys).
do_merge(M1, M2, [Head|Tail]) ->
   Check = check_if_same(M1, M2),
   io:fwrite("Check is: ~p\n", [Check]),
   case Check of 
     {ok, true} -> 
       io:fwrite("true\n");
     {ok, false} ->
       io:fwrite("false\n")
   end,
   do_merge(M1, M2, Tail);
do_merge(M1, M2, []) -> 
   ok.
check_if_same(M1, M2) -> 
   {ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.

给出以下输出:

Check is: {ok,true}
true
{"init terminating in do_boot",{{badmap,ok},[{maps,keys,[ok],[]},{helloworld,merger,2,[{file,"helloworld.erl"},{line,9}]},{init,start_em,1,[]},{init,do_boot,3,[]}]}}
init terminating in do_boot ()

Crash dump is being written to: erl_crash.dump...done

【问题讨论】:

  • 您能否接受我的回答或评论我没有正确解释的内容(或您不理解的内容),以便我可以编辑我的答案以在未来变得更好?谢谢:)

标签: erlang erlang-shell erlang-nif


【解决方案1】:

缺少一些信息。尽管此代码看起来像初稿或步骤,但它按预期工作。我在 shell 中对其进行了测试,得到了这个:

-module (merger).

-compile(export_all).

merger(M1, M2) ->
   M1_Keys = maps:keys(M1),
   M2_Keys = maps:keys(M2),
   do_merge(M1, M2, M1_Keys).
do_merge(M1, M2, [Head|Tail]) ->
   Check = check_if_same(M1, M2),
   io:fwrite("Check is: ~p\n", [Check]),
   case Check of 
     {ok, true} -> 
       io:fwrite("true\n");
     {ok, false} ->
       io:fwrite("false\n")
   end,
   do_merge(M1, M2, Tail);
do_merge(M1, M2, []) -> 
   ok.
check_if_same(M1, M2) -> 
   {ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.

test() ->
 merger(#{ "Bob" => #{"Sagget" => #{}} },#{ "Bob" => #{"Daniels" => #{}} }).

给出:

8> c(merger).
merger.erl:3: Warning: export_all flag enabled - all functions will be exported
merger.erl:7: Warning: variable 'M2_Keys' is unused
merger.erl:9: Warning: variable 'Head' is unused
merger.erl:19: Warning: variable 'M1' is unused
merger.erl:19: Warning: variable 'M2' is unused
{ok,merger}
9> merger:test().
Check is: {ok,true}
true
ok
10>

也许你也可以告诉我们合并ManOneManTwo的预期结果是什么

【讨论】:

    【解决方案2】:

    让我们先回顾一下您的问题,因为我们可以做出一些误解和/或更正。

    您的描述

    尽管看起来可以运行,但该程序正在崩溃。我不明白为什么。我正在尝试完成深度合并并需要条件逻辑。

    给定以下列表:

        ManOne = #{ "Bob" => #{"Sagget" => #{}} }
        ManTwo = #{ "Bob" => #{"Daniels" => #{}} }
    

    请注意,以上是不是列表,它们是地图,功能完全不同。

    从所有意图和目的来看,映射都是一个查找表,直到它包含约 31 个键/值对。

    此时,它变成了HashMap(这可以通过查看元素在map变成HashMap后变得无序来观察)。

    我正在尝试将它们进行如下比较,此函数按预期返回 true:

    check_if_same(M1, M2) -> 
       {ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.
    

    这是断言平等的错误方式;在 erlang 中,建议不要使用== 来检查相等性。

    应该使用=:=

    这是因为==确实检查它正在比较的元素的类型并且只采用模糊值 - 即1 == 1.0将返回true但@ 987654327@ 将返回 false。

    我个人建议改为使用 Erlang 的模式匹配来检查您的值。

    这可以使用以下 sn-p 来实现:

    -spec check_if_same(M1 :: map(), M2 :: map()) -> boolean().
    check_if_same(M1, M2) ->
        SortedKeys1 = lists:sort(maps:keys(M1)),
        SortedKeys2 = lists:sort(maps:keys(M2)),
        %% We hide the implementation of the function in
        %%  a function with the same name suffixed with an
        %%  underscore. This allows us to have a public api
        %%  but keep the implementation internal which allows
        %%  the code to be a bit cleaner.
        check_if_same_(SortedKeys1, SortedKeys2). 
    
    %% If they're both empty then we've gone through
    %%  every key meaning that they must be identical
    check_if_same_([], []) ->
        true;
    %% If the current Key on both heads is the same
    %%  then recurse and check the next and so on
    check_if_same_([Key|Tail1], [Key|Tail2]) ->
        check_if_same_(Tail1, Tail2);
    %% If we get anything else, e.g more keys in 
    %%  one than the other or the keys don't match,
    %%  then we'll fall in to this case. 
    %% As we know anything that falls in to this 
    %%  case doesn't match, we just return false
    check_if_same_(Keys1, Keys2) when is_list(Keys1), is_list(Keys2) ->
        false.
    

    请注意,在上面的 sn-p 中,我只返回了 truefalse - 我对更简洁代码的建议是保持以下格式;

    • ok - 这通常用于您关心效果而不是返回的函数
    • true | false - 这通常用于比较函数,即is_binary/1is_function/1
    • {ok, Value} - 这通常适用于您关心返回值的任何函数
    • {error, Reason} - 每当您预计会出现错误时都会使用此选项,以便您可以使用易于匹配的格式将错误冒泡到链上

    您的代码片段

    merger(M1, M2) ->
        M1_Keys = maps:keys(M1),
        %% Note that you don't use the M2Keys here so you don't need to do the work to get them
        M2_Keys = maps:keys(M2),
        do_merge(M1, M2, M1_Keys).
    do_merge(M1, M2, [Head|Tail]) ->
        Check = check_if_same(M1, M2),
        %% It's generally recommended to stick to io:format/2 rather than io:fwrite/2
        io:fwrite("Check is: ~p\n", [Check]), 
        case Check of 
            {ok, true} -> 
                io:fwrite("true\n");
            {ok, false} ->
                io:fwrite("false\n")
        end,
        do_merge(M1, M2, Tail);
    do_merge(M1, M2, []) -> 
        ok.
    check_if_same(M1, M2) -> 
        {ok, lists:sort( maps:keys(M1) ) == lists:sort( maps:keys(M2) )}.
    

    现在,上面的 sn-p(除了效率有点低)完全可以使用 erlang,并且可以按预期工作

    给出以下输出:

    Check is: {ok,true}
    true
    {"init terminating in do_boot",{{badmap,ok},[{maps,keys,[ok],[]},{helloworld,merger,2,[{file,"helloworld.erl"},{line,9}]},{init,start_em,1,[]},{init,do_boot,3,[]}]}}
    init terminating in do_boot ()
    
    Crash dump is being written to: erl_crash.dump...done
    

    这个故障转储是真正的问题所在;

    Check is: {ok,true}
    true
    

    由此我们可以看出我们

    • 点击 io:fwrite/2 (io:fwrite("Check is: ~p\n", [Check]))
    • 在案例中输入{ok, true}路径(io:fwrite("true\n")

    下一行是我们看到实际问题的地方,让我们分解一下:

    • "init terminating in do_boot" - 启动失败,可能是在运行 escript 或启动应用程序时

    现在让我们分解那个元组:

    {
        {badmap,ok}, %% The function we called expected a map and we passed in 'ok'
        [
            {maps,keys,[ok],[]}, %% We called maps:keys/1 with 'ok' as an arg
            {helloworld,merger,2,[{file,"helloworld.erl"},{line,9}]}, %% This was called at helloworld:merger/2 (helloworld.erl:9)
            {init,start_em,1,[]},{init,do_boot,3,[]} %% We failed on start up
        ]
    }
    

    我们可以从中得出的结论是,您在 helloworld.erlline 9 上使用无效值 ok 在代码中调用合并/p>

    【讨论】:

      猜你喜欢
      • 2021-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-26
      • 2016-06-09
      • 1970-01-01
      相关资源
      最近更新 更多