12

次のように定義された Erlang アクターがあるとします。

counter(Num) ->
  receive
    {From, increment} ->
      From ! {self(), new_value, Num + 1}
      counter(Num + 1);
  end.    

同様に、次のように定義された Ruby クラスがあります。

class Counter
  def initialize(num)
    @num = num
  end

  def increment
    @num += 1
  end
end

Erlang コードは、状態を維持するために末尾再帰を使用して、関数型スタイルで記述されています。しかし、この違いの意味のある影響は何ですか? 私の素朴な目には、これら 2 つのインターフェイスはほとんど同じように見えます。メッセージを送信すると、状態が更新され、新しい状態の表現が返されます。

関数型プログラミングは、OOP とはまったく異なるパラダイムであるとよく言われます。しかし、Erlang アクターは、状態を維持し、カプセル化し、メッセージベースのインターフェースを提供するという、オブジェクトが本来行うべきことを正確に行っているようです。

言い換えれば、Erlang アクター間でメッセージを渡す場合と、Ruby オブジェクト間でメッセージを渡す場合とでは、どのように違うのでしょうか?

機能/OOP の二分法には、私が見ているよりも大きな結果があるのではないかと思います。誰でもそれらを指摘できますか?

Erlang アクターが VM によってスケジュールされるため、他のコードと同時に実行される可能性があるという事実は脇に置いておきます。これが Erlang バージョンと Ruby バージョンの大きな違いであることは理解していますが、それは私が理解していることではありません。Ruby を含む他の言語でも同時実行が可能です。Erlang の同時実行性は非常に異なる (場合によってはより良い) パフォーマンスを発揮するかもしれませんが、私は実際にパフォーマンスの違いについて尋ねているわけではありません。

むしろ、私は質問の機能対 OOP 側にもっと興味があります。

4

5 に答える 5

11

言い換えれば、Erlang アクター間でメッセージを渡す場合と、Ruby オブジェクト間でメッセージを渡す場合とでは、どのように違うのでしょうか?

違いは、Ruby のような従来の言語ではメッセージ パッシングがなく、同じスレッドで実行されるメソッド呼び出しがあり、マルチスレッド アプリケーションを使用している場合、これにより同期の問題が発生する可能性があることです。すべてのスレッドは、互いのスレッド メモリにアクセスできます。

Erlang では、すべてのアクターは独立しており、別のアクターの状態を変更する唯一の方法はメッセージを送信することです。どのプロセスも、他のプロセスの内部状態にアクセスできません。

于 2013-03-01T06:21:26.790 に答える
0

Erlangには、Alan KayのOOP(Smalltalk)のメッセージパッシングアプローチとLispの関数型プログラミングが含まれています。

例で説明しているのは、OOPのメッセージアプローチです。メッセージを送信するErlangプロセスは、メッセージを送信するAlanKayのオブジェクトに似た概念です。ちなみに、並列実行オブジェクトがそれらの間でメッセージを送信するScratchでも実装されているこの概念を取得できます。

関数型プログラミングは、プロセスをコーディングする方法です。たとえば、Erlangの変数は変更できません。それらが設定されると、あなたはそれらを読むことしかできません。また、 Lispリストとほとんど同じように機能するリストデータ構造があり、Lispのラムダに刺激された楽しみがあります。

一方の側を通過するメッセージともう一方の側の機能は、Erlangではまったく2つの別個のものです。実際のerlangアプリケーションをコーディングする場合、98%の時間を関数型プログラミングに費やし、2%をメッセージの受け渡しについて考えます。これは主にスケーラビリティと同時実行性に使用されます。別の言い方をすれば、複雑なプログラミングの問題に取り組む場合、おそらくアーランのFP側を使用してアルゴの詳細を実装し、スケーラビリティや信頼性などのためにメッセージパッシングを使用します...

于 2013-03-01T10:14:17.553 に答える
0

外から見ると、役者は物に似ています。それらは状態をカプセル化し、メッセージを介して他の世界と通信してその状態を操作します。

FP がどのように機能するかを確認するには、アクターの内部を調べて、状態がどのように変化するかを確認する必要があります。状態が整数である例は単純すぎます。完全な例を提供する時間はありませんが、コードをスケッチします。通常、アクター ループは次のようになります。

loop(State) ->
  Message = receive
  ...
  end,
  NewState = f(State, Message),
  loop(NewState).

OOP との最も重要な違いは、変数の変更がないことです。つまり、NewState は State から取得され、ほとんどのデータをそれと共有できますが、State 変数は常に同じままです。

現在の状態を壊すことはないので、これは素晴らしいプロパティです。関数 f は通常、State を NewState に変換する一連の変換を実行します。そして、それが完全に成功した場合にのみ、loop(NewState) を呼び出して古い状態を新しい状態に置き換えます。したがって、重要な利点は、状態の一貫性です。

私が見つけた 2 番目の利点は、コードがすっきりしたことですが、慣れるまでに時間がかかります。通常、変数を変更することはできないため、コードを多数の非常に小さな関数に分割する必要があります。コードが適切に分解されるため、これは実際には素晴らしいことです。

最後に、変数を変更できないため、コードについて簡単に推論できます。変更可能なオブジェクトでは、オブジェクトの一部が変更されるかどうかを確認することはできず、グローバル変数を使用すると次第に悪化します。FP を実行しているときに、このような問題が発生することはありません。

これを試すには、純粋な erlang 構造 (actor、ets、mnesia、proc dict ではない) を使用して、より複雑なデータを機能的な方法で操作してみてください。または、これを使用してルビーで試すこともできます

于 2013-03-01T08:35:37.213 に答える
0

これについてどう思いますか:

thing(0) ->
   exit(this_is_the_end);
thing(Val) when is_integer(Val) ->
    NewVal = receive
        {From,F,Arg} -> NV = F(Val,Arg),
                        From ! {self(), new_value, NV},
                        NV;
        _ -> Val div 2
    after 10000
        max(Val-1,0)
    end,
    thing(NewVal).

プロセスをスポーンすると、値が 0 になるまで値が減少し、リンクされているプロセスにメッセージ {'EXIT',this_is_the_end} が送信されます。

ThingPid ! {self(),fun(X,_) -> X+1 end,[]}.
% which will increment the counter

また

ThingPid ! {self(),fun(X,X) -> 0; (X,_) -> X end,10}.
% which will do nothing, unless the internal value = 10 and in this case will go directly to 0 and exit

この場合、「オブジェクト」は、アプリケーションの残りの部分と並行して独自のライブを実行していること、ほとんどコードなしで外部と対話できること、および外部が彼にあなたがしなかったことを行うように依頼できることがわかります。いつコードを書いてコンパイルしたかわかりません。

これはばかげたコードですが、mnesia トランザクションのようなアプリケーションを実装するために使用される原則がいくつかあります... IMHO 概念は本当に異なりますが、正しく使用したい場合は別のことを考えなければなりません。Erlang で「OOP のような」コードを書くことは可能だと確信していますが、並行性を回避することは非常に困難です :o)。Erlang のアプリケーション アーキテクチャ (監視ツリー、「1 つのクライアント サーバー」のプール、リンクされたプロセス、監視対象のプロセス、そしてもちろんパターン マッチングの単一の割り当て、メッセージ、ノード クラスターなど) に関するいくつかのトラックを提供する OTP 原則を見てください。 )。

于 2013-03-01T13:09:14.473 に答える
0

私見ですが、これは FP と OOP の最良の例ではありません。違いは通常、オブジェクトのメソッド/関数へのアクセス/反復および連鎖で明らかになります。また、おそらく「現状」とは何かを理解することは、FPの方がうまくいくでしょう。

ここでは、2 つの非常に異なるテクノロジを相互に比較します。1 つはたまたま F で、もう 1 つは OO です。

すぐにわかる最初の違いは、メモリの分離です。メッセージは Erlang でシリアル化されるため、競合状態を回避しやすくなります。

2 つ目は、メモリ管理の詳細です。Erlang では、メッセージ処理は送信者と受信者の間で分割されます。Erlang VM が保持するプロセス構造のロックには 2 つのセットがあります。したがって、送信者がメッセージを送信している間、メイン プロセス操作をブロックしていないロックを取得します (メイン ロックによってアクセスされます)。要約すると、Ruby 側の完全にランダムな動作に対して、Erlang によりソフトなリアルタイムの性質が与えられます。

于 2013-03-01T00:29:05.397 に答える