【问题标题】:Erlang counter expression equivalent to C++, while loop?Erlang计数器表达式等效于C ++,while循环?
【发布时间】:2013-03-03 13:56:19
【问题描述】:

增量/减量运算符是否有等效表达式,例如counter++? 我也想知道如何正确地做到这一点?

-module(whileloop).
-export([call/0, while_loop/2]).

call() ->
    while_loop(10,0).

while_loop(Var,Counter) ->  
    case Var =:= Counter of
        false ->
            Counter += 1,
            whileloop(Var);
    end.

编辑:

 -module(whileloop).
-export([call/0, while_loop/2]).

call() ->
    while_loop(10,0).

while_loop(Var, Counter) -> 
    case Var =:= Counter of
        false ->            
            while_loop(Var,Counter + 1)
    end.

【问题讨论】:

    标签: erlang while-loop counter increment


    【解决方案1】:

    C += 1 的意思是修改C的值。在Erlang中是没有意义的,因为它只能给出如下结果:

    1> C = C+1.
    * 1: variable 'C' is unbound
    C = 1.
    1
    3> C = C+1.
    ** exception error: no match of right hand side value 2
    

    请记住,“A = B”并不意味着将 B 的值分配给 A,而是“模式匹配”A 与 B,

    • 如果 A 未绑定,则它将 B 的值绑定到 A;
    • 如果 A =:= B 什么都没做,则流程继续;
    • 如果 A =/= B 则进程崩溃。

    所以是的,如果你想要一个计数器或任何改变的信息,你必须使用一个状态变量,它作为递归循环的参数传递。从这个角度来看,你最后的代码是正确的,但是让我们看看当你在 shell 中调用“call()”时会发生什么。

    首先它在同一个进程 - 外壳 - 函数 while_loop(10,0) 中调用。

    10 不等于 0,所以它立即调用 while_loop(10,1)。

    10 不等于 1,所以它立即调用 while_loop(10,2)。

    以此类推,直到它调用 while_loop(10,10)。现在 10 =:= 10 为真,而且这个结果不匹配 case 的任何子句,所以你得到一个错误并且进程崩溃。

    由于您的代码不包含任何消息接收并且只是循环一个循环直到它崩溃,整个过程只需要几微秒,所以看起来它立即失败了。

    根据您的期望,您可以想象几种类型的计数器,这里有 2 个示例:

    -module(counter).
    
    -compile(export_all).
    
    % one counter that help you to count some events
    
    % interface
    
    start_c1(End) when is_integer(End) ->
        spawn(?MODULE,counter1,[End]).
    
    start_link_c1(End) when is_integer(End) ->
        spawn_link(?MODULE,counter1,[End]).
    
    inc_c1(Pid) when is_pid(Pid) ->
        Ref = make_ref(),
        Pid ! {inc,self(),Ref},
        receive
            {Ref,done} -> done;
            {Ref,V} -> V
        after 1000 ->
            {error,no_response}
        end.
    
    value_c1(Pid) when is_pid(Pid)  ->
        Ref = make_ref(),
        Pid ! {get_value,self(),Ref},
        receive
            {Ref,V} -> V
        after 1000 ->
            {error,no_response}
        end.
    
    stop_c1(Pid)  when is_pid(Pid) ->
        Pid ! stop.
    
    % the counter
    
    counter1(End) -> counter1_loop(End,0).
    
    counter1_loop(End,V) ->
        receive
            {inc,Pid,Ref} when V =/= done -> 
                NewV = case V+1 of
                    End -> done;
                    Nv -> Nv
                end,
                Pid ! {Ref,NewV},
                counter1_loop(End,NewV);
            {inc,Pid,Ref} ->
                Pid ! {Ref,done},
                counter1_loop(End,done);         
            {get_value,Pid,Ref} ->
                Pid ! {Ref,V},
                counter1_loop(End,V);
            stop ->
                ok
        end.
    
    % One kind of timeout that execute something after a while - 
    % note it exists a similar one in the library
    
    start_after(T,M,F,A) when is_integer(T), is_list(A) ->
        Ref = make_ref(),
        {Ref,spawn(?MODULE,after_receive,[T,M,F,A,self(),Ref])}.
    
    cancel_after(P) when is_pid(P) ->
        P ! cancel.
    
    
    % the counter
    after_receive(T,M,F,A,Pid,Ref) ->
        receive
            {cancel,Ref} -> Pid ! {after_receive,Ref,cancelled}
        after T ->
            Pid ! {after_receive,Ref,done},
            apply(M,F,A)
        end.
    

    以及如何使用它们:

    1> c("../src/counter").
    {ok,counter}
    2> {Ref,P} = counter:start_after(5000,io,format,["This is the end!" ]).
    {#Ref<0.0.0.29>,<0.33.0>}
    This is the end!3> 
    3> {Refa,Pa} = counter:start_after(50000,io,format,["This is the end!" ]).
    {#Ref<0.0.0.34>,<0.35.0>}
    4> Pa ! {cancel,Refa}.
    {cancel,#Ref<0.0.0.34>}
    5> flush().
    Shell got {after_receive,#Ref<0.0.0.29>,done}
    Shell got {after_receive,#Ref<0.0.0.34>,cancelled}
    ok
    6> P1 = counter:start_c1(5).
    <0.52.0>
    7> counter:inc_c1(P1).
    1
    8> counter:inc_c1(P). 
    {error,no_response}
    9> counter:inc_c1(P1).
    2
    10> counter:inc_c1(P1).
    3
    11> counter:value_c1(P1).
    3
    12> counter:inc_c1(P1).  
    4
    13> counter:inc_c1(P1).
    done
    14> counter:value_c1(P1).
    done
    15> counter:inc_c1(P1).  
    done
    16> counter:stop_c1(P1).
    stop
    17> counter:inc_c1(P1). 
    {error,no_response}
    18> 
    

    【讨论】:

      【解决方案2】:

      您编辑的版本没有 Var =:= Counter 时的子句,因此会崩溃。而且你最好在函数子句中使用模式匹配。

      -module(whileloop).
      -export([call/0, while_loop/2]).
      
      call() ->
          while_loop(10,0).
      
      while_loop(Var, Var) ->
          ok;
      while_loop(Var, Counter) -> 
          while_loop(Var, Counter + 1).
      

      当然,您需要在循环内做一些事情。您可以为此使用 lambda:

      -module(whileloop).
      -export([call/0, while_loop/2]).
      
      call() ->
          while_loop(10, 0, fun(Counter) -> io:format("Counter: ~p~n", [Counter]) end).
      
      while_loop(Var, Var, _) ->
          ok;
      while_loop(Var, Counter, Fun) ->
          Fun(Counter),
          while_loop(Var, Counter + 1).
      

      【讨论】:

        【解决方案3】:

        只需递归调用 while_loop 并将 Counter 参数加一即可:

        while_loop(Var, Counter + 1)
        

        【讨论】:

        • 好的,但是 counter++ 的等价性如何,以防万一我需要在其他地方知道这一点。
        • 我在上面的代码中尝试了while_loop(Var, Counter + 1),它是非法模式?我删除了Counter += 1,这一行。
        • @lost_with_coding 在 Erlang 中没有。您不能像在 Erlang 中那样更改变量的值。阅读本文了解更多信息:stackoverflow.com/questions/9753080/…
        • 你确实在它后面保留了分号;?并删除Counter += 1 行?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-24
        • 2018-10-30
        • 2016-04-02
        • 1970-01-01
        • 1970-01-01
        • 2019-06-03
        相关资源
        最近更新 更多