1

次の例で異なる結果を生成するために、C#とRが異なるスコープルールを誰かが指摘できますか?

C#

public static void Main(string[] args)
{
    var funs = new Action<string>[5];
    for (int i = 0; i < 5; i++)
    {
        int j = i;
        {
            funs[j] = x => Console.WriteLine(j);
        }
    }
    funs[2]("foo"); // prints 2
}

R

funclist <- list()
for(i in 1:5)
{
  {
    j<-i
    funclist[[j]] <- function(x) print(j)
  } // the 5 inner blocks share the same scope?
}

funclist[[2]]('foo') // prints 5

アップデート

この質問は役に立ちますR: ローカル変数の使用法

私は違いを見つけたと思います-Rには、ブロックが新しいスコープを作成すると言っているドキュメントはありません。したがって、関数のみが新しいスコープを作成できます。 for ループは作成できません。ブラケットは作成できません。

関数以外のブラケットが新しいスコープを作成しないことを明確に述べているRの決定的なガイドへのリンクを教えてくれる人はいますか?

4

5 に答える 5

4

についてはよくわかりませんC#が、 についてRは、関数が遅延評価されます。

jinは、print(j)関数が呼び出されるまで評価されません。したがって、最後に停止したときは5 だった の値検索するfunclist[[2]]('foo')だけです。ただし、次のことを試してみると、値がループにまったく関係していないことがわかります。Rj

j <- "Some Other Value"
funclist[[2]]('foo')

コメントの質問に関する更新

ではR、for ループはスコープや環境に影響しません。(一方で、functionsループのapplyように)。したがって、明確にするために、ネストされた*環境*内で変数を初期化し、環境からアクセスすることはできません[1]。ただし、反対のこともできます。つまり、子環境内から親環境のオブジェクトにアクセスします。

ロス・イハーカ & ロバート・ジェントルマン Ross Ihaka の署名ソースのこの写真にはっきりと示されているように : http://www.nytimes.com/2009/01/07/technology/business-computing/07program.html?pagewanted=all

R でのスコープの詳細については、http: //cran.r-project.org/doc/contrib/Fox-Companion/appendix-scope.pdfを参照してください。

[1] 上記の記述は完全に真実ではありません。関数を使用して、そのような変数にアクセスできますget(.., envir=..)。ただし、ネストされた関数内から親のオブジェクトにアクセスできる方法で直接アクセスすることはできません。

于 2013-04-26T14:48:33.393 に答える
3

@RicardoSaportaの答えを少し拡張します。

R で次のコマンドを実行します。

environment(funclist[[2]])

関数の親環境がグローバル環境であることがわかります。したがってj、関数に対してローカルになることはないため、実行時にローカル環境で名前が付けられた変数が見つからないjため、次のことになります行うことは、グローバル環境である囲み環境を調べ、jそこにある現在の値を使用することです。その後、いずれかrm(j)の関数を実行すると、見つからないというエラーが発生しますj

各関数に異なる値を持つ独自の囲み環境を持たせたい場合は、次のjようにすることができます。

funclist <- list()
for(i in 1:5)
{
   funclist[[i]] <- local( {
    j <- i
    function(x) print(j)
    } )
}

funclist[[2]]('foo') # prints 2
于 2013-04-26T15:29:47.993 に答える
3

R については何も知りませんが、ここで C# で何が起こっているかを説明できます。

C# では、関数を入力すると、その関数がアクティブ化されます。その「アクティベーション」は、その関数の呼び出しによって使用されるすべての変数と一時的な値の状態を追跡します。これが関数を再帰的にする方法です。各呼び出しは独自のアクティベーションを取得するため、独自のローカル変数値を持つことができます。

C# では、関数が独自のアクティベーションを取得するというこの考え方は、実際には blocks に拡張されています。あなたが言う時:

public static void Main(string[] args)
{
    var funs = new Action<string>[5];
    for (int i = 0; i < 5; i++)
    {
        int j = i;
        {
            funs[j] = x => Console.WriteLine(j);
        }
    }
    funs[2]("foo"); // prints 2
}

それはあなたが言ったかのように論理的には

public static void Main(string[] args)
{
    var funs = new Action<string>[5];
    for (int i = 0; i < 5; i++)
        D(i, funs);
    funs[2]("foo"); // prints 2
}

static void D(int i, Action<string>[] funs)
{
    int j = i;
    {
        funs[j] = x => Console.WriteLine(j);
    }
}

ループを通過するたびに、ラムダが閉じる新しい変数を取得します。j

R で起こっていることはj、ループを通過するたびに新しい を取得する代わりに、前の を更新してjいることだと思います。これは、C# が行うこととはまったく異なります。つまり、R プログラムは実際には次のものと同等のようです。

public static void Main(string[] args)
{
    var funs = new Action<string>[5];
    int j;
    for (int i = 0; i < 5; i++)
    {
        j = i;
        {
            funs[j] = x => Console.WriteLine(j);
        }
    }
    funs[2]("foo");
}

これは、j が取る最後の値であるため、4 を出力します。

詳細については、 http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/を参照してください。

于 2013-04-26T15:27:10.033 に答える
2

R 言語定義のセクション 3.5では、Rの変数のスコープ規則について説明しています。

セクション3.5.2では、それは述べています

関数を呼び出すたびに、関数で作成されたローカル変数を含むフレームが作成され、環境で評価され、組み合わせて新しい環境が作成されます。

したがって、新しいレキシカル スコープを作成するのは (のみ) 関数の呼び出しです。関数内では、他の関数を呼び出して追加のスコープを作成できます (実際、local()新しいスコープを作成することのみを目的とする関数については、関数のヘルプを参照してください)。

そのため、R でのスコープは、C++ とは異なり、ブロック レベルではなく関数レベルでのみ行われます。

于 2013-04-26T19:57:01.163 に答える
1

C#実行方法は手続き的だと思います。すべての関数の引数は、リストの作成時に評価されます。

関数内の引数は、R関数が初めて呼び出されたときにのみ評価されます。つまり、funclist[[2]]('foo')が呼び出されたとき、forループはすでに評価されてjおり、 last valueを持っています5

注: forcewhich を使用すると、C#同様の動作が得られます。

于 2013-04-26T15:23:03.603 に答える