3

私はまだErlangに不慣れで、変数を変更できないことに頭を悩ませようとしています。スタックを作成し、新しい要素を追加したいとします。リストに新しい値を割り当てることができない場合、スタックを更新するにはどうすればよいですか?毎回新しいリストを作成する必要がありますか?

たとえば、プッシュは次のように見えると思っていました

List = [X|List].

そして、ポップは

[Head|Tail] = List
Head
List = Tail

もちろん、Listの値を変更できず、問題があるため、これは機能しません。どんな助けでも大歓迎です。

4

2 に答える 2

4

Erlangは、関数型プログラミング言語に共通する機能である関数内に副作用を持たせることはできません。可変状態の変更は副作用です。Erlangの状態変化はすべて、プロセスとメッセージパッシングによって隠されています。これはいわゆるアクターモデルです。

変数を「変更」する一般的な方法は、関数が変更された変数を使用して自分自身を呼び出すことです。これは再帰と呼ばれます。たとえば、リストのすべての要素を合計するには、次のようにします。

sum([]) -> 0;
sum([H|Tail]) -> H + sum(Tail).

さらに良いのは、関数の末尾再帰を作成することです。これは、関数が関数本体の最後の命令として自分自身を呼び出すことを意味します。すべての関数呼び出しをスタックに保持する必要がないため、メモリを節約できます(末尾呼び出しの最適化)。同じ例ですが、末尾再帰を使用しています。

sum([], Acc) -> Acc;
sum([H|Tail], Acc) -> sum(Tail, Acc + H).

sum(L) -> sum(L, 0).

この例では、中間結果を渡すためにアキュムレータ変数を導入しました。

プログラムの副作用をなくす方法は必ずしも明らかではありません。特に、(CやJavaのように)手続き的な用語で問題を考えようとする場合はそうです。より理論的なレベルで関数型プログラミングを理解するには、練習とおそらく意志が必要です。

純粋に関数型プログラミング言語には、副作用はまったくありません。関数の戻り値は、関数の入力引数のみに基づいている必要があり、関数からの出力は戻り値のみである必要があります。これはErlangには当てはまりません。recieve句と!演算子は、関数内の入力と出力に使用されます。副作用。外部状態は、メッセージを送信して応答を取得できるプロセスとして保持できます。

メッセージパッシングによって値を変更できる変数を作成する方法の例を次に示します(var_proc末尾再帰であるかどうかを確認してください)。

var_proc(Value) ->
    receive
    {get, Pid} ->
            Pid ! {value, Value},
            var_proc(Value);
        {set, New} -> 
            var_proc(New)
    end.

var_start(Init) ->
    spawn(?MODULE, var_proc, [Init]).

var_set(Pid, Value) ->
    Pid ! {set, Value}.

var_get(Pid) ->
    Pid ! {get, self()},
    receive
    {value, Value} -> Value
    end.

これを使用する方法の例を次に示します(私はモジュールを「合計」と呼んでいます)。

13> V = sum:var_start(6).
<0.72.0>
14> sum:var_get(V).
6
15> sum:var_set(V, 10).
{set,10}
16> sum:var_get(V).    
10

その他の例といくつかの動機は、Erlangドキュメントの並行プログラミングの章にあります。

于 2013-02-16T16:18:37.307 に答える
3

Erlangのような関数型プログラミング言語のスタックデータ構造の場合、複数のプロセスが同時にアクセスする必要がない限り、通常、メッセージを含むプロセスを格納して操作を行うための個別のプロセスを作成する手間はかかりません。

スタックの変更されたバージョンを含み、そこからそれを使用するために、毎回新しい変数を割り当てることが期待されます。

new() -> [].

push(Value, OldStack) -> [Value|OldStack].

pop([]) -> empty;
pop([Value|RestStack]) -> {Value, RestStack}.

次のように使用します。

S0 = new(),
S1 = push(a, S0),
S2 = push(b, S1),
S3 = push(c, S2),
...
case pop(SN) of
   empty -> stack_was_empty;
   {Element, SN1} ->
     do_something_with(Element),
     SN2 = push(b, SN1),
     ...
...
于 2013-02-17T12:22:08.733 に答える