F# と C# の記法比較

MSDN F# リファレンスを使い C# と記法を比較する

コレクション型

F# コレクション型 F# Collection Types

元ネタ http://msdn.microsoft.com/ja-jp/library/hh967652.aspx

System.Collections.Generic の名前空間の要素などの .NET Framework とは異なります。 具体的には、配列のコレクションにのみ変更可能な要素があります。

  • List 順序付きで、変更不能な、同じ型の要素の列です。
  • 配列 サイズ固定で、0オリジンで、変更可能な、同じ型の連続した要素のコレクションです。
  • シーケンス シーケンスは IEnumerableのエイリアスです seq<'T> の型で表されます。
  • マップ 要素のディクショナリ。 要素はキーによってアクセスします。
  • Set 比較には、キー値の IComparable のインターフェイスの実装を使用する F# の構造的な比較関数である

関数の表

コレクションに対する関数群。非常に多いが、LINQ のメソッドか STL のように覚えると効率が良いかも。

  • Append 2 番目のコレクションの要素に続く最初のコレクションの要素を含む新しいコレクションを返します。
  • add 追加された要素を持つ新しいコレクションを返します。
  • average コレクション内の要素の平均を返します。
  • averageBy 各要素に適用される指定された関数の結果の平均を返します。
  • blit 配列のセクションをコピーします。
  • cache シーケンスの数の要素に格納します。
  • cast 指定された型に要素を変換します。
  • choose 指定された関数 f をリストの各要素 x に適用します。 Some(f(x))関数がを返す各要素の結果を含むリストを返します。
  • collect 指定された関数をコレクションの各要素に適用し、すべての結果を連結して、結合リストを返します。
  • compareWith 指定された比較関数、要素ごとに、要素を使用して 2 二つのシーケンスを比較します。
  • concat 指定された列挙体の列挙体を、1 つの連結された列挙体として結合します。
  • contains 設定した要素が含まれている場合、true を返します。
  • containsKey 要素がマップのドメインにあるかどうかをテストします。
  • count セット内の要素数を返します。
  • countBy キー生成関数をシーケンスの各要素に適用し、元のシーケンスの生成の一意キーと数を格納するシーケンスを返します。
  • Copy コレクションをコピーします。
  • Create 最初にある特定の値である要素全体の配列を作成します。
  • delay 指定したシーケンスの遅延指定から作成されたシーケンスを返します。
  • difference 最初のセットから削除される 2 番目のセットの要素を含む新しいセットを返します。
  • distinct エントリの汎用ハッシュと等価比較に従って、重複するエントリを含まないシーケンスを返します。 要素がシーケンス内に複数回発生した場合、それ以降の生成は破棄されます。
  • distinctBy 指定されたキー生成関数はを返します。キーの汎用ハッシュと等価比較に従って、重複するエントリを含まないシーケンスを返します。 要素がシーケンス内に複数回発生した場合、それ以降の生成は破棄されます。
  • Empty 空のコレクションを作成します。
  • exists シーケンスのすべての要素が、指定された述語を満たすかどうかをテストします。
  • exists2 のペアの入力シーケンスの対応する要素が、指定された述語を満たすかどうかをテストします。
  • fill 特定の値に配列の要素の範囲を設定します。
  • filter 指定された述語が trueを返すコレクションの要素のみを含む新しいコレクションを返します。
  • Find 指定した関数が true を返す最初の要素を返します。 そのような要素が存在しない場合は、KeyNotFoundException を返します。
  • findIndex 指定した述語を満たす配列内の最初の要素のインデックスを返します。 述語を満たす要素が KeyNotFoundException を発生させます。
  • findKey コレクション内の各マッピングで関数を評価し、true関数がを返す最初のマッピングのキーを返します。 そのような要素が存在しない場合、この関数は KeyNotFoundException を発生させます。
  • fold 計算にアキュムレータ引数を使用しながら、コレクションの各要素に関数を適用します。 f 入力関数がで、要素が i0…場合、この関数の計算 f (… f (s) i0…)。
  • fold2 計算にアキュムレータ引数を使用しながら、2 つのコレクションの対応する要素に関数を適用します。 各コレクションは同じサイズである必要があります。 f 入力関数がで、要素が i0 j0…、… jN 場合、この関数の計算 f (… f (s) i0 j0…) jN。
  • foldBack 計算にアキュムレータ引数を使用しながら、コレクションの各要素に関数を適用します。 f 入力関数がで、要素が i0…場合は、を計算 i0 f (…) の f (s)。
  • foldBack2 計算にアキュムレータ引数を使用しながら、2 つのコレクションの対応する要素に関数を適用します。 各コレクションは同じサイズである必要があります。 f 入力関数がで、要素が i0 j0…、… jN 場合は、を計算 i0 f (j0… jN の f (s) )。
  • forall コレクションのすべての要素が、指定された述語を満たすかどうかをテストします。
  • forall2 コレクションの対応するすべての要素が、指定された述語をペア単位で満たすかどうかをテストします。
  • get/nth インデックスを持つコレクションから要素を返します。
  • head コレクションの最初の要素を返します。
  • init ディメンション、およびジェネレーター関数を持つコレクションの要素を計算するために作成します。
  • initInfinite シーケンスが生成されます、は、一連の要素特定の関数を呼び出して、反復処理されるとき。
  • intersect 2 セットの積集合を計算します。
  • intersectMany セットのシーケンスの積集合を計算します。 シーケンスは空である必要があります。
  • isEmpty コレクションが空の場合 true を返します。
  • isProperSubset 最初のセットのすべての要素が 2 番目のセットに含まれ、2 番目のセットに少なくとも 1 個の要素は最初のセットにありません true を返します。
  • isProperSuperset 2 番目のセットのすべての要素が最初のセットに存在する場合は、最初の 1 文字以上の要素が 2 番目のセットに存在しない true を返します。
  • isSubset 最初のセットのすべての要素が 2 番目のセットに存在 true を返します。
  • isSuperset 2 番目のセットのすべての要素が最初のセットに存在 true を返します。
  • iter 指定された関数をコレクションの各要素に適用します。
  • iteri 指定された関数をコレクションの各要素に適用します。 関数に渡される整数は要素のインデックスを示します。
  • iteri2 2 種類の配列内の一致するインデックスの要素のペアに、指定された関数を適用します。 関数に渡される整数は要素のインデックスを示します。 2 種類の配列は同じ長さである必要があります。
  • iter2 2 種類の配列内の一致するインデックスの要素のペアに、指定された関数を適用します。 2 種類の配列は同じ長さである必要があります。
  • Length コレクションの要素数を返します。
  • map 要素が配列の各要素に指定された関数を適用し、その結果コレクションを構築します。
  • map2 要素に 2 個のコレクションの対応する要素に指定された関数をペアに適用し、その結果コレクションを構築します。 2 個の入力配列は同じ長さである必要があります。
  • map3 要素に 3 個のコレクションの対応する要素に指定された関数を同時に適用し、その結果コレクションを構築します。
  • mapi 要素が配列の各要素に指定された関数を適用した結果である配列を作成します。 関数に渡される整数のインデックスは、変換する要素のインデックスを示します。
  • mapi2 要素に 2 個のコレクションの対応する要素に指定された関数をペアに適用し、その結果を要素のインデックスを渡しますコレクションを構築します。 2 個の入力配列は同じ長さである必要があります。
  • max [最大値] の演算子を使用して比較されるコレクションの最大の要素を返します。
  • maxBy 関数の結果を [最大値] を使用して比較されるコレクションの最大の要素を返します。
  • maxElement 指示に従ってセットの大きな要素が使用されますセットに返します。
  • min [最小値] の演算子を使用して比較されるコレクション最小の要素を返します。
  • minBy 関数の結果を [最小値] の演算子を使用して比較されるコレクション最小の要素を返します。
  • minElement 指示に従ってセットの最小要素が使用されますセットに返します。
  • ofArray 指定された配列と同じ要素を含むコレクションを作成します。
  • ofList 指定されたリストと同じ要素を含むコレクションを作成します。
  • ofSeq 指定されたシーケンスと同じ要素を含むコレクションを作成します。
  • pairwise 2 番目の要素の先行処理としてのみを返す最初の要素を除いた入力シーケンスの各要素と元のシーケンスを返します。
  • partition 2 種類のコレクションに分割します。 最初のコレクションは、指定した述語がを返す true、2 番目のコレクションは false指定した述語がを返す要素が含まれます要素が含まれています。
  • permute 指定された置換に従ってすべての要素を置換した配列を返します。
  • pick 関数が Some を返す最初の結果を返す一連の要素に指定された関数を適用します。 関数は一部を返す、KeyNotFoundException が発生します。
  • readonly 指定されたシーケンスへオブジェクトのシーケンスとデリゲート オブジェクトを作成します。 この操作は型のキャストを元のシーケンスを再発見し、変更できなくなります。 たとえば、配列は、返されたシーケンスは配列の要素を返しますが、配列に返されたシーケンス オブジェクトをキャストできません。
  • reduce 計算にアキュムレータ引数を使用しながら、コレクションの各要素に関数を適用します。 最初の要素は 2 個、パスに関数を適用することにより、この関数の先頭 3 番目の要素とともに関数の結果など。 最後に、最終結果を返します。
  • reduceBack 計算にアキュムレータ引数を使用しながら、コレクションの各要素に関数を適用します。 f 入力関数がで、要素が i0…場合は、を計算 i0 f (…内部 f (1) )。
  • remove マップのドメインから要素を削除します。 例外は、要素が存在しない場合
  • replicate すべての要素を指定された値に設定して、指定の長さのリストを作成します。
  • rev 要素の順序を反転した新しいリストを返します。
  • scan 計算にアキュムレータ引数を使用しながら、コレクションの各要素に関数を適用します。 この操作は、2 番目の引数とリストの最初の要素に関数を適用します。 操作は 2 番目の要素とともに関数にこの結果などを渡します。 最後に、操作には、中間結果と最終結果のリストを返します。
  • scanBack foldBack 操作は、中間結果と最終結果似ています。
  • singleton 1 項目だけを格納するシーケンスを返します。
  • Set ある値の配列の要素を設定します。
  • skip 基になるシーケンスの N 個の要素をスキップし、シーケンスの残りの要素を格納するシーケンスを返します。
  • skipWhile 指定された述語が true を返し、シーケンスの残りの要素を格納するとき、反復処理されると、基になるシーケンスの要素をバイパス シーケンスを返します。
  • sort 要素の値を使用してコレクションを並べ替えます。 要素は [比較]を使用して比較されます。
  • sortBy 指定されたプロジェクションを提供するキーを使用して、指定したリストを並べ替えます。 キーは [比較]を使用して比較されます。
  • softInPlace これを変更し、指定された比較関数を使用して配列の要素を並べ替えます。 要素は [比較]を使用して比較されます。
  • sortInPlaceBy これを変更し、キーに指定されたプロジェクションを使用して配列の要素を並べ替えます。 要素は [比較]を使用して比較されます。
  • sortInPlaceWith 順序としてファイルを変更し、指定された比較関数を使用して配列の要素を並べ替えます。
  • sortWith 順序と新しいコレクションを返すこととして指定された比較関数を使用して、コレクションの要素を並べ替えます。
  • sub 開始インデックスと長さで指定されたサブ範囲を含む配列を作成します。
  • sum コレクションの要素の合計を返します。
  • sumBy コレクションの各要素に関数を適用することで生成された結果の合計を返します。
  • tail 最初の要素を除いたリストを返します。
  • take 指定された数までシーケンスの要素を返します。
  • takeWhile 指定した述語がを返す true 説明し、これ以上の要素を返す間、反復処理されると、基になるシーケンスの要素がシーケンスを返します。
  • toArray 指定されたコレクションから配列を作成します。
  • toList 指定されたコレクションからリストを作成します。
  • toSeq 指定されたコレクションからシーケンスを作成します。
  • truncate 列挙と N 要素よりも返されないシーケンスを返します。
  • tryFind 指定した述語を満たす要素を検索します。
  • tryFindIndex そのような要素が存在しない場合、指定された述語を満たすだけ、一致する要素のインデックスを返す最初の要素の検索、または None。
  • tryFindKey 指定した述語を満たすコレクション内の最初のマッピングのキーを返します。そのような要素が存在しない場合は、None を返します。
  • tryPick 関数は値の Some を返す最初の結果を返す一連の要素に指定された関数を適用します。 そのような要素が存在しない場合、操作は Noneを返します。
  • unfold 指定された計算を生成する要素を含むシーケンスを返します。
  • Union 2 つのセットの和集合を計算します。
  • unionMany セットのシーケンスの和集合を計算します。
  • unzip ペアのリストを 2 つのリストに分割します。
  • unzip3 3 要素の組のリストを 3 つのリストに分割します。
  • windowed 入力シーケンスから取得されたコンテナー要素のスライド式ウィンドウを生成するシーケンスを返します。 各ウィンドウは、新しい配列として返されます。
  • zip ペアのリストに 2 個のコレクションを結合します。 2 つのリストは同じ長さである必要があります。
  • zip3 3 要素の組のリストに二つのコレクションを結合します。 各リストは同じ長さである必要があります。

タプル

タプル Tuples

ネタ元 http://msdn.microsoft.com/ja-jp/library/dd233200.aspx

タプルには、2 個タプル、3 個タプルなどがあり、型が同じである場合と異なる場合があります。

// Tuple of two integers.
( 1, 2 ) 

// Triple of strings.
( "one", "two", "three" ) 

// Tuple of unknown types.
( a, b ) 

// Tuple that has mixed types.
( "one", 1, 2.0 ) 

// Tuple of integer expressions.
( a + 1, b + 1) 

Tuple of unknown types 以外は、new Tuple で作成する。

// Tuple of two integers.
var t1 = new Tuple<int, int>(1, 2);
// Triple of strings.
var t2 = new Tuple<string, string, string>("one", "two", "three");
// Tuple that has mixed types.
var t4 = new Tuple<string, int, double>("one", 1, 2.0);
// Tuple of integer expressions.
int a = 10, b = 20;
var t5 = new Tuple<int, int>(a + 1, b + 1);

個別の値の取得

次のコードに示すように、パターン マッチを使用して、タプルの要素の名前へのアクセスおよび割り当てを行うことができます。

let print tuple1 =
   match tuple1 with
    | (a, b) -> printfn "Pair %A %A" a b

パターンマッチではないが、C# では .Item1, .Item2 でタプルの要素を取得する。

public void print( Tuple<int,int> t )
{
    Console.WriteLine("Pair {0} {1}", t.Item1, t.Item2);
}

次のように、let 束縛で組のパターンを使用できます。

let (a, b) = (1, 2)

C# の場合はタプルを作成して、.Item1, .Item2 で取得すればよい。

var t = Tuple.Create<int, int>(1, 2);
int a = t.Item1, b = t.Item2;

タプルの 1 つの要素のみが必要な場合は、ワイルドカード文字 (アンダースコア) を使用して、不要な変数の新しい名前が作成されないようにすることができます。

let (a, _) = (1, 2)

C# の場合は .Item1 だけ取ればよい。

var t = Tuple.Create<int, int>(1, 2);
int a = t.Item1;

fst 関数および snd 関数はそれぞれ、タプルの最初の要素と 2 番目の要素を返します。

let c = fst (1, 2)
let d = snd (1, 2)

これも .Item1, .Item2 を使えばよい。

let third (_, _, c) = c

C# の場合は、.Item3 で取得する。

var t = Tuple.Create<int, int>(1, 2, 3);
int c = t.Item3;

タプルの使用

タプルによって、関数から複数の値を返す便利な方法が提供されます。

let divRem a b = 
   let x = a / b
   let y = a % b
   (x, y)

2つの値を戻すためにタプルを使うのは C# でも使うことができる。

public Tuple<int, int> DivRem(int a, int b)
{
    int x = a / b;
    int y = a % b;
    return new Tuple<int, int>(x, y);
}

タプルは、通常の関数構文が意味する関数の引数が暗黙でカリー化されないようにする場合に、関数の引数として使用することもできます。

let sumNoCurry (a, b) = a + b

この手法は C# のメソッドを呼び出すときにも使う。

タプル型の名前

タプルである型の名前を記述する場合は、* 記号を使用して要素を区切ります。 (10, 10.0, "ten") のように、int、float、および string で構成されるタプルについては、次のように型を記述します。

int * float * string

スペースは -> で、タプルは * で、表現する。「関数プログラミング」では、スペースは →(矢印) 、タプルは ×(直積)で表す。

文字列

文字列 Strings

元ネタ http://msdn.microsoft.com/ja-jp/library/dd323829.aspx

エスケープシーケンスの扱いは C# と同じ。

@ 記号の後に続くリテラルは、逐語的文字列です。 この場合、2 つの引用符文字が 1 つの引用符文字として解釈されることを除いて、エスケープ シーケンスはすべて無視されます。

また、文字列は三重の引用符で囲まれている場合があります。

F#

let data = 
    """
    ここで複数行のデータを書ける。
    XMLデータとかを文字列で書けるので良いらしい。
    """

@ 記号を使った書き方

F#

// Using a verbatim string
let xmlFragment1 = @"<book author=""Milton, John"" title=""Paradise Lost"">"

// Using a triple-quoted string
let xmlFragment2 = """<book author="Milton, John" title="Paradise Lost">"""

C# も @ 記号で文字列をエスケープできる。 C#

string xmlFragment1 = @"<book author=""Milton, John"" title=""Paradise Lost"">";

1文字だけ出力する。

F#

printfn "%c" str1.[1]

C# の場合は、string.Format を使う。

C#

Console.WriteLine("{0}", str1[1] );

部分文字列を表示する

F#

printfn "%s" (str1.[0..2])
printfn "%s" (str2.[3..5])

F# の意味としては、それぞれの要素を指定しているので範囲とは異なるのだが、C# の場合は文字列に対しては直に Substring を使う。

C#

Console.WriteLine("{0}", str1.Substring(0,3));
Console.WriteLine("{0}", str2.Substring(3,3));

リテラル文字列が ASCII 文字列であることを示すには、サフィックス B を追加します。

F#

// "abc" interpreted as a Unicode string.
let str1 : string = "abc"
// "abc" interpreted as an ASCII byte array. 
let bytearray : byte[] = "abc"B 

Unicode(Char)ではなくて、ASCII(byte)という違いがある。

C# の場合は、文字列は Char で扱うので byte 配列に変換する拡張メソッドを用意する。

C#

public void test()
{
    string str1 = "abc";
    byte[] bytearray = "abc".B();
}

public static class StringExtentions
{
    public static byte[] ToByteArray( this string str )
    {
        var src = str.ToArray<char>();
        var dest = new byte[str.Length];
        for ( int i=0; i<dest.Length; i++ ) {
            dest[i] = (byte)src[i];
        }
        return dest;
    }
    public static byte[] B(this string str) { return str.ToByteArray(); }
}

文字列演算

文字列を連結するには、+ 演算子または ^ 演算子を使用する 2 つの方法があります。

F#

let string1 = "Hello, " + "world"

これは C# も同じ。

C#

var string1 = "Hello, " + "world";

String クラス

String の Chars プロパティを使用すると、次のコードに示すように、インデックスを指定して文字列中の各文字にアクセスできます。

F#

let printChar (str : string) (index : int) =
    printfn "First character: %c" (str.Chars(index))

Chars プロパティはないが、そのまま Char 配列として扱える。

C#

void printChar(string str, int index)
{
    Console.WriteLine("First character: %c", str[index]);
}

unit 型

unit 型 Unit Type

元ネタ http://msdn.microsoft.com/ja-jp/library/dd483472.aspx

unit 型は、特定の値を持たないことを示す型です。unit 型には値が 1 つだけあり、他の値が存在しない場合や不要な場合のプレースホルダーとして機能します。

F#

// The value of the unit type.
()

すべての F# の式は、評価して値にする必要があります。 式で目的の値が生成されない場合は、unit 型の値が使用されます。 unit 型は、C#、C++ などの言語の void 型に似ています。

ということなので、F# で () を返している関数は void にしておけばよい。

F#

let function1 x y = x + y
// The next line results in a compiler warning.
function1 10 20 
// Changing the code to one of the following eliminates the warning.
// Use this when you do want the return value.
let result = function1 10 20
// Use this if you are only calling the function for its side effects,
// and do not want the return value.
function1 10 20 |> ignore

F# の場合、戻り値を無視するには明示的に ignore を指定することになる。 C# の場合は、無視しても警告はでない。

C#

int function1(int x, int y) { return x + y; }

void test()
{
    // 警告は出ない
    function1(10, 20);
    // 戻り値を取得
    var result = function1(10, 20);
    // 戻り値を無視する
    function1(10, 20);
}

プリミティブ型

プリミティブ型 Primitive Types

元ネタ http://msdn.microsoft.com/en-us/library/dd233210.aspx

省略。

F# では、float, double が Double になっていることぐらい。なので、F# では実数型として「float」をよく使う。

  • nativeint IntPtr 符号付き整数としてのネイティブ ポインターです。
  • unativeint UIntPtr 符号なし整数としてのネイティブ ポインターです。

型推論

型推論

型推論 Type Inference

元ネタ http://msdn.microsoft.com/ja-jp/library/dd233180.aspx

パラメーターおよび戻り値の型の推論

たとえば、次のコードでは、パラメーターの型 a および b と戻り値の型は、いずれも int と推論されます。これは、リテラルの 100 が int 型であるためです。

F#

let f a b = a + b + 100

C# の場合、引数の型推論はできないので、普通に関数を作る。

C#

int f(int a, int b)
{
    return a + b + 100;
}

特定の型でのみ使用できる関数やメソッドなど、型に対する制限を意味する他の構成要素を使用して、型推論を操作することもできます。 わざと曖昧さを除去することもできる。

F#

// Type annotations on a parameter.
let addu1 (x : uint32) y =
    x + y

// Type annotations on an expression.
let addu2 x y =
    (x : uint32) + y

すべてのパラメーターの後ろに型の注釈を記入して、関数の戻り値を明示的に指定することもできます。

F#

let addu1 x y : uint32 =
   x + y

一般的にパラメーターの型の注釈が役立つケースには、パラメーターがオブジェクト型であり、メンバーを使用する場合があります。 この場合、string が指定されていないと、'a になるので、Replace メソッドを指定したときにコンパイルエラーになる。

F#

let replace(str: string) =
    str.Replace("A", "a")

例えば、T のままでは .Replace メソッドが使えないが、string で制約することで .Replace が使えるようになる...のだが、これはコンパイルが通らない。何か良い例はないか?

C#

class StringEx<T> where T : string
{
    public string replace( T str )
    {
        return str.Replace("A", "a");
    }
}

自動ジェネリック

次の関数は、任意の型の 2 つのパラメーターを組み合わせて 1 つのタプルにします。

F#

let makeTuple a b = (a, b)

型は、次のように推論されます。

F#

'a -> 'b -> 'a * 'b

C# の場合には引数に型推論が適用できないが、少し楽にタプルを作ることもできる。

C#

class TupleEx<T>
{
    public static Tuple<T, T> MakeTuple(T a, T b)
    {
        return new Tuple<T, T>(a, b);
    }
}

public void test()
{
    // int 型のタプル
    var t1 = TupleEx<int>.MakeTuple(10, 20);
    // string 型のタプル
    var t2 = TupleEx<string>.MakeTuple("masuda", "tomoaki"); 
}

リテラル

リテラル literal

元ネタ http://msdn.microsoft.com/ja-jp/library/dd233193.aspx

リテラル型

省略。"text"B で ASCII の byte[] を返すのが、それらしい。

F# 3.1 では、文字列リテラルを結合するために + 符号を使用できます。 また、列挙型フラグを結合するにはビットごとの OR 演算子 (|||) を使用できます。 たとえば、F# 3.1 では次のようなコードが有効です。

[<Literal>]
let literal1 = "a" + "b"

[<Literal>]
let fileLocation =   __SOURCE_DIRECTORY__ + "/" + __SOURCE_FILE__

[<Literal>]
let literal2 = 1 ||| 64

[<Literal>]
let literal3 = System.IO.FileAccess.Read ||| System.IO.FileAccess.Write

C# の場合は OR 演算子は(|)を利用する。enum の or も作れる。 定数扱いにする場合は、

const string literal1 = "a" + "b";
// const string fileLocation = __SOURCE_DIRECTORY__ + "/" + __SOURCE_FILE__;
const int literal2 = 1 | 64 ;
const System.IO.FileAccess literal3 = 
    System.IO.FileAccess.Read | System.IO.FileAccess.Write;