20

あれは何度も読んだ

F# またはその他の .NET 言語から生成されたアセンブリは (ほとんど) 区別できません。

その後、.NET 4 (ベータ 2) で F# と C# の相互運用性を試していました。次のクラスを使用して、新しいソリューションと C# プロジェクトを作成しました。

public class MyClass {
    public static int Add(int a, int b) { return a + b; }
}

次に、F# プロジェクトで、C# プロジェクトを参照した後、次のことを試しました。

MyClsas.Add(4, 5) |> printfn "%d" // prints 9 (no kidding!)

ここまでは順調ですね。それから、私が何度も読んだ別の文 (おそらく別の本で) が頭に浮かびました。

他の .NET ライブラリから関数に引数を渡す場合、".MethodName(parm1, parm2)" のような構文を使用します。つまり、パラメーターはタプルとして渡されます。

OPがlikeを使用して作成しようとしていた質問で、SOでここで読んだことがあります(しかし、リンクすることができませんでした[ 4, 5, 6 ][4; 5; 6]

「コンマは「タプル作成演算子」です。それ以外はすべてセミコロンを使用します。」

次に、クラスを次のように変更しました。

public class MyClass {
    public static int Add(int a, int b) { return a + b; }
    public static int Add(Tuple<int, int> a) { return a.Item1; }
}

今、私はF#でそれを使用しようとしました:

MyClass.Add(4, 5) |> printf "%d" // prints ... (keep reading!)

したがって、上記の 3 つの引用を合計すると、次のように結論付けることができます。

  • F# は、表示されるとタプルを作成します。(4, 5)
  • 次に、オーバーロードを呼び出しますAdd(Tuple<int, int>)
  • したがって、4が出力されます

驚いたことに、それは 9を出力しました。面白くないですか?

ここで実際に何が起こっているのでしょうか? 上記の引用とこの実際の観察は矛盾しているようです。F# の「推論」を正当化し、可能であれば MSDN のドキュメントを参照することはできますか?

ありがとう!

編集

(さらに情報を追加するには(Blindyの回答から))

もしあなたがそうするなら:

MyClass.Add((4, 5)) |> printfn "%d" // prints 9

F# はAdd(Tuple<int, int>)オーバーロードを呼び出します。

ただし、これを使用して別の F# プロジェクト (別のアセンブリ) を作成する場合:

namespace MyFSharpNamespace
type MyFShapClass = class
    static member Add x y = x + y
    end

このようにC#で使用できます

public static void Main(string[] args) {
    MyFSharpNamespace.MyFSharpClass.Add(4, 5);
}

ここまでは順調ですね。ここで、F# から (別のプロジェクト、別のアセンブリから) 使用しようとすると、次のことを行う必要があります。

MyFSharpNamespace.MyFSharpClass.Add 4 5 |> printfn "%d"

引数を as として渡すと、isと not の(4, 5)ため、F# はコンパイルされません。Addint -> int -> int(int * int) -> int

何が起こっている?!?

4

4 に答える 4

19

他の.NETライブラリから関数に引数を渡すときは、「。MethodName(parm1、parm2)」のような構文を使用します。つまり、パラメーターはタプルとして渡されます。

それよりも恐ろしいです。言語仕様のメソッドオーバーロード解決海峡の説明を参照してください。

基本的に、メソッド呼び出しの引数は実際にはタプルではないということです。これは構文タプルであり、何かのコンマ区切りのリストを意味しますが、括弧はメソッド呼び出し構文の一部であり、コンマも同様です。たとえば、o.M(a=1, b=2)2つのブール値のタプルを使用するメソッド呼び出しではなく、2つの名前付き引数を使用するのはそのためです。

したがって、通常、コンマで区切られたすべてのコンポーネントは、個別の引数にマップされます。したがって、なぜオーバーロードを呼び出し、をAdd(1, 2)呼び出します。ここにあいまいさはありません。Add(int, int)Add((1, 2))Add(Tuple<int, int>)

ただし、特定のケースに適した特殊なケースは次のとおりです。

名前付きの実際の引数がなく、に候補メソッドが1つだけあり、Mオプションではない引数を1つだけ受け入れる場合、argタプル形式への分解は無視され、それ自体argが実際の名前の1つになりargます。

したがって、タプルを除くすべてのオーバーロードを削除すると、突然、括弧内のすべてが呼び出しでタプルコンストラクターとして効果的に扱われます。ただし、たとえば2つのオーバーロードがAdd(int)あり、、の場合Add(Tuple<int,int>)、フォームの呼び出しはAdd(1,2)まったく解決されません。

于 2010-01-08T08:12:38.613 に答える
3

現在、F# はインストールされていませんが、

MyClass.Add(4, 5) |> printf "%d"

9を出力しますが、

MyClass.Add((4, 5)) |> printf "%d"

印刷されます.. 4ですよね?二重括弧に注意してください。内側のペアはタプルをマークし、外側のペアは関数呼び出しをマークしています。

于 2010-01-08T06:22:43.903 に答える
3

それは単なるコンパイラの魔法です。

let add a b = a+b 

addにコンパイルされるためadd(a,b)、C# から簡単に呼び出すことができます。ただし、F# プログラムadd a bでは、IL の属性が原因であると見なされます。

F# で C# 関数を呼び出す場合、C# 関数には 1 つのパラメーター (要素によって正しいオーバーロードが決定されるタプル) しかないと考えると役立つ場合があります。したがって、次のように書くことができます。

// MyClass.Add(5,3) = 8
let eight = (5,3) |> MyClass.Add
于 2010-01-08T07:35:11.917 に答える
0

私は F# の専門家ではないので、少し的外れかもしれませんが、タプルの F# の概念は BCLSystem.Tuple型と相関していないと思います。タプルは F# の中心的な原則であり、言語に組み込まれていますが、C#、VB.NET、およびその他のほとんどの .NET 言語はタプルをネイティブにサポートしていません。タプルはこれらの言語で役立つ可能性があるため、ライブラリはそれらのサポートを獲得しています。

F# のタプルは、C# やその仲間のメソッドにパラメーターが渡されるのとほぼ同じ方法で、メモリ内で表現されるという私の推測をさらに推し進めます。つまり、それらは本質的にコンポーネントの値配列です。この値配列がメソッド呼び出しのためにスタックにプッシュされると、そのメソッドが C# から呼び出された場合と同様に、その構成要素のそれぞれをスタックにプッシュするのと同じ効果があります。

したがって、2 番目の例では、F# タプルを作成し、それをスタックにプッシュしてからAdd、タプルに含まれる型を受け取るオーバーロードを呼び出します。

とにかく、それは私の推測です。おそらく私よりも F# を使ったことがあるので、これ以上の洞察があるかもしれません。また、生成されたコードReflectorを見ることで、追加の手がかりを得ることができます。

于 2010-01-08T06:36:16.247 に答える