Unable to use function call in function guard

2019-02-12 18:06发布

问题:

I'm new to Erlang and am trying to program a bounded-buffer problem program. It is almost working, except for making sure the producers don't get too far ahead and overwrite unconsumed data. To handle this, I decided to try putting guards on my buffer() function so that I could have a version w/o receive used when the buffer is full, a version w/o send used when the buffer is empty, and a normal version for the rest of the time.

My problem is that the guard for the receiver-less version requires me to know the size of the array representing the buffer, which requires a call to array:size/1. Apparently, Erlang does not allow function invocations in guards, which prevents this from working. Is there some way to work around this without changing the function declaration for my buffer actor?

%% buffer: array num num
%% A process that holds the shared buffer for the producers and consumers
buffer(Buf, NextWrite, NextRead) when NextWrite == NextRead ->
    io:format(" * ~w, ~w, ~w~n", [array:to_list(Buf), NextRead, NextWrite]),
    receive
        {enqueue, Reply_Pid, Num} ->
            io:format("~w: > ~w~n", [Reply_Pid, Num]),
            buffer(array:set(NextWrite rem array:size(Buf), Num, Buf), NextWrite + 1, NextRead);
        finish ->
            io:format("finished printing~n")
    end;
buffer(Buf, NextWrite, NextRead) when (NextWrite - NextRead) == array:size(Buf) ->
    io:format(" * ~w, ~w, ~w~n", [array:to_list(Buf), NextRead, NextWrite]),
    receive
        {dequeue, Reply_Pid} ->
            io:format("~w: < ~w~n", [Reply_Pid, array:get(NextRead rem array:size(Buf), Buf)]),
            Reply_Pid ! {reply, array:get(NextRead rem array:size(Buf), Buf)},
            buffer(Buf, NextWrite, NextRead + 1);
        finish ->
            io:format("finished printing~n")
    end;
buffer(Buf, NextWrite, NextRead) ->
    io:format(" * ~w, ~w, ~w~n", [array:to_list(Buf), NextRead, NextWrite]),
    receive
        {dequeue, Reply_Pid} ->
            io:format("~w: < ~w~n", [Reply_Pid, array:get(NextRead rem array:size(Buf), Buf)]),
            Reply_Pid ! {reply, array:get(NextRead rem array:size(Buf), Buf)},
            buffer(Buf, NextWrite, NextRead + 1);
        {enqueue, Reply_Pid, Num} ->
            io:format("~w: > ~w~n", [Reply_Pid, Num]),
            buffer(array:set(NextWrite rem array:size(Buf), Num, Buf), NextWrite + 1, NextRead);
        finish ->
            io:format("finished printing~n")
    end.

回答1:

There are only certain functions that can be used in a guard, see Guard Sequences in the Erlang manual. You can easily do what you need as follows:

buffer(Buf, NextWrite, NextRead) -> buffer(Buf, NextWrite, NextRead, array:size(Buf)).

buffer(Buf, NextWrite, NextRead, _) when NextWrite == NextRead -> 
  ;
buffer(Buf, NextWrite, NextRead, BufSize) when (NextWrite - NextRead) == BufSize ->
  ;
buffer(Buf, NextWrite, NextRead, _) ->
  .


回答2:

As Geoff Reedy has mentioned there are only few BIFS that are allowed in guards.

But the guardian parse transform library can be used to call any function in guards.



标签: erlang