2017-10-05 9 views

答えて

6

=オペレータは、割り当てアサーション両方です。私はこれを行う場合は

A = 1, 
A = 2, 

私のプログラムがクラッシュします。私はただA = 1と言いました。Aがバインドされていない(まだラベルとして存在していない)場合、実行の範囲が変更されるまで、値1が永久に割り当てられます。だから私はそれをA = 2Aの値をアサートしようとすると2ではないと言います。だから私たちは悪い試合でクラッシュする。

  • 現在の関数の定義:Erlangで

    範囲は二つのものによって定義されます。このスコープは、関数定義の間、絶対です。

  • 現在のラムダまたはリストの理解の定義。このスコープはラムダに対してローカルですが、参照される外側のスコープの値を超えても閉じます。

これらのスコープはは常に彼らは外側のスコープにあるものでを宣言された時点でを代わらあります。これが匿名関数を使ってクロージャを作る方法です。たとえば、データのリストを送信したいソケットがあるとします。ソケットはすでに関数の先頭にある変数名Socketにバインドされています。リスト操作を使用して値のリストをマップし、その特定のソケットを介して送信される副作用に送信します。

send_stuff(Socket, ListOfMessages) -> 
    Send = fun(Message) -> ok = gen_tcp:send(Socket, Message) end, 
    lists:foreach(Send, ListOfMessages). 

リスト操作の各反復:私は「いくつかのデータを送信する」のより一般的な操作のうち、その値をカリー化する効果があるラムダの本体、内ソケットの値を超える閉じることができますlists:foreach/2は、第1引数としてアーリー1の関数しか受け入れることができません。私たちはSocketの値を既に内部で取得している(これは既に外側スコープにバインドされていたため)クロージャを作成し、バインドされていない内部変数Messageと組み合わせています。また、gen_tcp:send/2がラムダ内で毎回働いているかどうかを確認するには、戻り値gen_tcp:send/2、実際にはokだったと主張します。

これは、超有用な特性です。

だから、このことを念頭に置いて、のはあなたのコードを見てみましょう:あなた上記のコードで

1> Total = 15.  
2> Calculate = fun(Number)-> Total = 2 * Number end. 
3> Calculate(6). 

はちょうどあなたがちょうど私達のような、その値のラベルを(作成している意味、Totalに値を割り当てました上記の例ではSocketが割り当てられていました)。結果は15.0、ない15になるので、Totalはそう2 * 7.5はどちらかそれをカットしないと、整数だったので本当であることはできません - その後、あなたはTotalの値は2 * Numberの結果があるかもしれないものは何でもあるを主張しています。この例では

1> Calculate = fun(Number)-> Total = 2 * Number end. 
2> Total = 15. 
3> Calculate(6). 

は、しかし、あなたは外側のスコープで宣言された任意の値を超える閉じないTotal呼ば内部変数を持っています。後でTotalと呼ばれる外側スコープ内のラベルを宣言するですが、今回は最初の行のラムダ定義が抽象関数に変換され、ラベルTotalが不変のスペースに完全に渡されています新しい関数定義のCalculateへの代入が表されます。したがって、競合はありません。

はリスト内包から内側の値を参照しようとすると、例えば、何が起こるか考えてみましょう:

1> A = 2. 
2 
2> [A * B || B <- lists:seq(1,3)]. 
[2,4,6] 
3> A. 
2 
4> B. 
* 1: variable 'B' is unbound 

これは、あなたがPythonの2、たとえば、に期待するものではありません。

>>> a = 2 
>>> a 
2 
>>> [a * b for b in range(1,4)] 
[2, 4, 6] 
>>> b 
3 

ちなみに、これはPythonの3で修正されています:

>>> a = 2                                                                  
>>> a                                                                   
2                                                                    
>>> [a * b for b in range(1,4)] 
[2, 4, 6]                                                                  
>>> b                                                                   
Traceback (most recent call last):                                                           
    File "<stdin>", line 1, in <module>                                                           
NameError: name 'b' is not defined 

(そして、私はJavaScripを提供しますたとえ比較のための例であっても、スコープ規則はそれほど絶対に狂っていてもそれは問題ではない...)

5

の一致なし、変数されています変更不可能ですが、シェルではを書くと、実際には変数Totalを作成しませんが、シェルはあなたが持っている動作を模倣するために最善を尽くしますあなたはアプリケーションを実行しており、テーブルにカップル{"Total",15}を格納しています。

次の行で、fun計算を定義します。パーサーは式Total=2*Numberを見つけ、そのテーブルを通ってTotalが以前に定義されたことを検出します。評価は15 = 2*Numberに相当するものに変わります。

あなたが定義するときに、3行目に、あなたはCalculate(6)を評価するように依頼する場合、それは計算になり、15 = 2*6を評価し、エラーメッセージ2番目の例で

exception error: no match of right hand side value 12

を発行し、総額がまだ定義されていません関数。関数は代入なしで格納されます(合計はもう使用されません)。少なくとも、グローバル変数への代入はありません。したがって、合計を定義するときに競合はなく、評価するときにエラーは発生しません。Calculate(6).

動作はコンパイル済みモジュールとまったく同じです。 Erlangで

+1

これは、より明確な答えであるため、範囲(コンテキスト)に関する情報を追加する必要があります。 「検索表」は必ずしも理解しやすいものではありません。いくつかのリンク:http://learnyousomeerlang.com/higher-order-functions、http://www.erlang.org/course/advanced#scope、http://icai.ektf.hu/pdf/ICAI2007-vol2-pp137 -145.pdf –

+0

@Atomic_alarm:あなたが正しいのではないか:o)シェルの動作とモジュールコードの小さな違いがあるので、実際にはこのテーブル(シェルと並行して動作するプロセス辞書)特にシェル内の変数f(Foo)を「忘れる」ことが可能であるという事実を私は常に抱きしめてきました。しかし、それは正しいです、それは私の答えを理解するのに役立つ、私はモジュール内で同じ動作することを言及する必要があると感じた... – Pascal

1

変数 'Total'にはすでに値15が割り当てられているため、変数名2行目の合計。他の名前に変更する必要があります。Total1またはTotal2 ...

関連する問題