32

「ErlangとOTPの動作」の本から理解したように、動作という言葉は次のことを指します。

  • 関数のセットである動作インターフェース。
  • アプリケーション固有のコード(コールバックモジュール)である動作の実装。
  • プロセスである動作コンテナ。

質問:

Erlang / OTPの初心者は行動について何を知っておくべきですか?OTPの動作の概念を簡単に説明して理解することは可能ですか?

Elang / OTPのコンテキストで実際に「コールバック関数」とはどういう意味ですか?

ビヘイビアー実装のコールバックを、Javaでオーバーライドされたメソッドと見なすことができますか?

この本は、次のコードのライブラリ関数'gen_server:start_link / 4'に関連付けられているコールバック関数は'Module:init/1'であると述べています。

これは、init / 1でgen_server:start_link / 4ライブラリ関数を呼び出すことを意味しますか?それとも他に何か意味がありますか?

-module(tr_server).

-behaviour(gen_server).

-include_lib("eunit/include/eunit.hrl").

%% API
-export([
         start_link/1,
         start_link/0,
         get_count/0,
         stop/0
         ]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 1055).

-record(state, {port, lsock, request_count = 0}).


%%%===================================================================
%%% API
%%%===================================================================


%%--------------------------------------------------------------------
%% @doc Starts the server.
%%
%% @spec start_link(Port::integer()) -> {ok, Pid}
%% where
%%  Pid = pid()
%% @end
%%--------------------------------------------------------------------
start_link(Port) ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).

%% @spec start_link() -> {ok, Pid}
%% @doc Calls `start_link(Port)' using the default port.
s    tart_link() ->
    start_link(?DEFAULT_PORT).

%%--------------------------------------------------------------------
%% @doc Fetches the number of requests made to this server.
%% @spec get_count() -> {ok, Count}
%% where
%%  Count = integer()
%% @end
%%--------------------------------------------------------------------
get_count() ->
    gen_server:call(?SERVER, get_count).

%%--------------------------------------------------------------------
%% @doc Stops the server.
%% @spec stop() -> ok
%% @end
%%--------------------------------------------------------------------
stop() ->
    gen_server:cast(?SERVER, stop).


%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

init([Port]) ->
    {ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
    {ok, #state{port = Port, lsock = LSock}, 0}.

handle_call(get_count, _From, State) ->
    {reply, {ok, State#state.request_count}, State}.

handle_cast(stop, State) ->
    {stop, normal, State}.

handle_info({tcp, Socket, RawData}, State) ->
    do_rpc(Socket, RawData),
    RequestCount = State#state.request_count,
    {noreply, State#state{request_count = RequestCount + 1}};
handle_info(timeout, #state{lsock = LSock} = State) ->
    {ok, _Sock} = gen_tcp:accept(LSock),
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

do_rpc(Socket, RawData) ->
    try
        {M, F, A} = split_out_mfa(RawData),
        Result = apply(M, F, A),
        gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
    catch
        _Class:Err ->
            gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
    end.

split_out_mfa(RawData) ->
    MFA = re:replace(RawData, "\r\n$", "", [{return, list}]),
    {match, [M, F, A]} =
        re:run(MFA,
               "(.*):(.*)\s*\\((.*)\s*\\)\s*.\s*$",
                   [{capture, [1,2,3], list}, ungreedy]),
    {list_to_atom(M), list_to_atom(F), args_to_terms(A)}.

args_to_terms(RawArgs) ->
    {ok, Toks, _Line} = erl_scan:string("[" ++ RawArgs ++ "]. ", 1),
    {ok, Args} = erl_parse:parse_term(Toks),
    Args.


%% test

start_test() ->
    {ok, _} = tr_server:start_link(1055).
4

5 に答える 5

22

他の回答が既に行っているように特定の質問に対処しようとするのではなく、行動の背後にある基本を簡単な言葉で説明し、それらの基本を理解することに基づいて独自の質問に答えられるようにします.

動作は基本的にメッセージ処理フレームワークです。「フレームワーク」とは、エンド ユーザーが完了およびカスタマイズできる、問題に対する部分的な解決策の古典的な定義を意味します。OTP ビヘイビアーは、基本的に以下を提供します。

  • メッセージループ
  • コードのアップグレード、トレース、システム メッセージなどの基礎となる OTP サポートとの統合。

ビヘイビアは、メッセージ処理をコールバック モジュールに委譲するか、「Erlang and OTP In Action」が呼び出すようなビヘイビアの実装を行います。関数の呼び出し時にinit/1、コールバック モジュールは通常、メッセージ ループが代わりに保持する状態を作成します。動作ループは、コールバック モジュール メッセージ処理関数の後続の各呼び出しにこの状態を渡します。これらの各呼び出しは、変更された状態を返すことができます。コールバック関数は、ビヘイビア メッセージ ループに次に何をすべきかを伝える命令も返します。

以下は、動作の中心にあるメッセージ ループの大幅に簡略化されたバージョンです。

loop(Callbacks, State) ->
  {Next, NState} =
 receive
                     M1 ->

                       Callbacks:handle_m1(M1,State);
                     M2 ->
                       Callbacks:handle_m2(M2,State);
                     Other ->
                       Callbacks:handle_other(Other,State)
                   end,
  case Next of

    stop -> ok;
    _ -> loop(Callbacks, NState)
  end.

この末尾再帰ループには、引数としてCallbacksモジュールとState変数があります。このループが最初に呼び出される前に、コールバック モジュールが何であるかをすでにビヘイビアに伝えており、ベース OTP ビヘイビア サポート コードがinit/1コールバック関数を呼び出して の初期値を取得していますState

この動作ループの例では、フォームM1M2、およびその他のメッセージ (詳細はここでは関係ありません) を受信し、メッセージごとに、Callbacksモジュール内の異なるコールバック関数を呼び出します。この例では、コールバック関数handle_m1とコールバック関数がそれぞれhandle_m2メッセージM1とメッセージを処理し、コールバック関数が他のすべての種類のメッセージを処理します。が各コールバック関数に渡されることに注意してください。各関数は、ループに次に何をするかを伝える最初の要素と、ループの可能な新しい状態 (と同じ値または新しい異なる値) を含む 2 番目の要素を持つタプルを返すことが期待されます。これは、ループが変数に格納します。この例では、ifはアトムです。M2handle_otherStateStateNStateNextstop、ループは停止しますが、それ以外の場合は、ループが再帰的に呼び出され、新しい状態NStateが次の反復に渡されます。また、末尾再帰であるため、ループがスタックを吹き飛ばすことはありません。

やなどの標準的な OTP 動作のソースを掘り下げるgen_servergen_fsm、このようなループが見つかりますが、システム メッセージ、タイムアウト、トレース、例外などを処理するため、はるかに複雑です。標準的な動作も開始します。別のプロセスでループするため、ループ プロセスを開始してメッセージを渡すためのコードも含まれています。

于 2011-04-26T21:48:29.327 に答える
21

Q: Erlang/OTP の初心者が動作について知っておくべきことは何ですか? OTP 動作の概念を一言で説明して理解することは可能ですか?

アプリケーション/スーパーバイザー/gen_server/gen_event/gen_fsm などの動作に応じてコンパイラがより直感的なエラー メッセージを生成できるように、動作は通常コードで使用されます。

これにより、コンパイラは、ex: gen_server の動作に固有のエラー メッセージを表示できます。

Q: Elang/OTP のコンテキストで実際に「コールバック関数」とは何を意味しますか?

コールバック関数は、GUI プログラミングから取られたと言えます (少なくとも似ています)。exのイベントが発生するたびに。マウス クリック マウス クリックを処理する別の関数があります。

したがって、いつでも。gen_server のエクスポートされた関数が別のモジュールから呼び出された場合、その関数は異なるパターンを持つコールバック関数 (handle_call/handle_cast) を持つことができます。

Q:ビヘイビアー実装のコールバックを、Java でオーバーライドされたメソッドと見なすことはできますか?

ええ...多分...いいえ:)

Q:次のコードのライブラリ関数「gen_server:start_link/4」に関連付けられているコールバック関数は「Module:init/1」であると本に書かれています。

gen_server:start_link は、w55 によって回答されたように、init 関数を単独で呼び出します.... (申し訳ありませんが、かなり大きな名前です)。


私はあなたのすべての質問に答えたことを願っています:)

于 2011-04-26T14:34:23.327 に答える
12

Erlang/OTP の初心者が動作について知っておくべきことは?

おそらくここに書いてあること。

OTP 動作の概念を一言で説明して理解することは可能ですか?

ドキュメントからの読み取り:「動作は、これらの一般的なパターンの形式化です。アイデアは、プロセスのコードを一般的な部分 (動作モジュール) と特定の部分 (コールバック モジュール) に分割することです。」

Elang/OTP のコンテキストで実際に「コールバック関数」とは何を意味しますか?

コールバック関数の例が提供されている上記のリンクを見てください。

振る舞いの実装におけるコールバックを、Java でオーバーライドされたメソッドと見なすことはできますか?

Java 用語では、動作はおそらく Java インターフェースであり、コールバックはインターフェースで定義されたメソッドの 1 つの実装です。

この本には、次のコードのライブラリ関数「gen_server:start_link/4」に関連付けられているコールバック関数は「Module:init/1」であると書かれています。init/1 で gen_server:start_link/4 ライブラリ関数を呼び出すということですか? それとも別の意味ですか?

これは、gen_server:start_link/4 を呼び出すたびに、関数 Module:init/1 が呼び出されることを意味します。ここで、Module は start_link 関数に渡した 2 番目のパラメーターであり、4 番目の引数として指定した引数を使用します。言い換えれば、これは start_link/4 の舞台裏で起こっていることです:

...
start_link(Name, Module, Args, Opts) ->
  ...
  Module:init(Args)
  ...
...
于 2011-04-26T17:57:10.763 に答える
3

erlang lib ディレクトリにある gen_server モジュールのソース コードを見てください。ソースコードで非常によく説明されており、コメントは非常に精巧です。

于 2011-04-26T14:45:10.287 に答える
0

gen_server:start_link は init を呼び出します。

于 2011-04-26T13:20:17.787 に答える