C# Language
機能プログラミング
サーチ…
機能とアクション
Funcは、パラメータ化された匿名関数の保持者を提供します。先頭の型は入力であり、最後の型は常に戻り値です。
// square a number.
Func<double, double> square = (x) => { return x * x; };
// get the square root.
// note how the signature matches the built in method.
Func<double, double> squareroot = Math.Sqrt;
// provide your workings.
Func<double, double, string> workings = (x, y) =>
string.Format("The square of {0} is {1}.", x, square(y))
アクションオブジェクトはvoidメソッドと似ていますので、入力タイプのみです。評価スタックに結果はありません。
// right-angled triangle.
class Triangle
{
public double a;
public double b;
public double h;
}
// Pythagorean theorem.
Action<Triangle> pythagoras = (x) =>
x.h = squareroot(square(x.a) + square(x.b));
Triangle t = new Triangle { a = 3, b = 4 };
pythagoras(t);
Console.WriteLine(t.h); // 5.
不変性
不変性は関数型プログラミングではよくあり、オブジェクト指向プログラミングではまれです。
たとえば、可変状態のアドレスタイプを作成します。
public class Address ()
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
}
コードの任意の部分は、上記のオブジェクト内の任意のプロパティを変更する可能性があります。
次に、不変のアドレスタイプを作成します。
public class Address ()
{
public readonly string Line1;
public readonly string Line2;
public readonly string City;
public Address(string line1, string line2, string city)
{
Line1 = line1;
Line2 = line2;
City = city;
}
}
読み取り専用のコレクションを持つことは、不変性を尊重するものではないことに注意してください。例えば、
public class Classroom
{
public readonly List<Student> Students;
public Classroom(List<Student> students)
{
Students = students;
}
}
オブジェクトのユーザがコレクションを変更することができるので(オブジェクトを追加または削除することができるため)、変更不可能ではありません。不変にするには、IEnumerableなどのインターフェイスを使用するか、追加するメソッドを公開しないか、またはReadOnlyCollectionにする必要があります。
public class Classroom
{
public readonly ReadOnlyCollection<Student> Students;
public Classroom(ReadOnlyCollection<Student> students)
{
Students = students;
}
}
List<Students> list = new List<Student>();
// add students
Classroom c = new Classroom(list.AsReadOnly());
不変オブジェクトの場合、次のような利点があります。
- それは既知の状態になります(他のコードでは変更できません)。
- スレッドセーフです。
- コンストラクタは、検証のための単一の場所を提供します。
- オブジェクトを変更できないことがわかっているので、コードを理解しやすくなります。
NULL参照を避ける
C#開発者は多くのnull参照例外を処理します。 F#開発者は、Optionタイプを持っているので、そうではありません。オプション<>型(おそらく名前として<>を好むもの)では、Some型とNone型が返されます。これは、メソッドがnullレコードを返す可能性があることを明示します。
例えば、あなたは以下を読んで、あなたがヌル値を扱わなければならないかどうかを知ることはできません。
var user = _repository.GetUser(id);
可能なnullについて知っていれば、それに対処する定型コードを導入することができます。
var username = user != null ? user.Name : string.Empty;
Option <>が代わりに返されたらどうなりますか?
Option<User> maybeUser = _repository.GetUser(id);
このコードでは、Noneレコードが返され、SomeまたはNoneをチェックするための定型コードが必要であることが明示されています。
var username = maybeUser.HasValue ? maybeUser.Value.Name : string.Empty;
次のメソッドは、Option <>を返す方法を示しています。
public Option<User> GetUser(int id)
{
var users = new List<User>
{
new User { Id = 1, Name = "Joe Bloggs" },
new User { Id = 2, Name = "John Smith" }
};
var user = users.FirstOrDefault(user => user.Id == id);
return user != null ? new Option<User>(user) : new Option<User>();
}
Option <>の最小実装です。
public struct Option<T>
{
private readonly T _value;
public T Value
{
get
{
if (!HasValue)
throw new InvalidOperationException();
return _value;
}
}
public bool HasValue
{
get { return _value != null; }
}
public Option(T value)
{
_value = value;
}
public static implicit operator Option<T>(T value)
{
return new Option<T>(value);
}
}
上記の回避策を示すために、 avoidNull.csxはC#REPLで実行できます。
前述のように、これは最小の実装です。 「多分」NuGetパッケージを検索すると、数多くの優れたライブラリが登場します。
高次関数
上位関数とは、別の関数を引数として取るか、関数を返す関数です(またはその両方)。
これは、LINQのWhere句に述語を渡す場合など、lambdaで一般的に行われます。
var results = data.Where(p => p.Items == 0);
Where()句には、かなりの柔軟性を与える多くの異なる述語を受け取ることができます。
メソッドを別のメソッドに渡すことは、ストラテジーデザインパターンを実装するときにも見られます。たとえば、実行時の要件に応じて、さまざまなソート方法を選択し、オブジェクトのSortメソッドに渡すことができます。
不変なコレクション
System.Collections.Immutable
NuGetパッケージは、不変のコレクションクラスを提供します。
アイテムの作成と追加
var stack = ImmutableStack.Create<int>();
var stack2 = stack.Push(1); // stack is still empty, stack2 contains 1
var stack3 = stack.Push(2); // stack2 still contains only one, stack3 has 2, 1
ビルダーを使用して作成する
特定の不変なコレクションには、大きな不変インスタンスを安価に構築するために使用できるBuilder
内部クラスがあります。
var builder = ImmutableList.CreateBuilder<int>(); // returns ImmutableList.Builder
builder.Add(1);
builder.Add(2);
var list = builder.ToImmutable();
既存のIEnumerableから作成する
var numbers = Enumerable.Range(1, 5);
var list = ImmutableList.CreateRange<int>(numbers);
すべての不変なコレクションの種類のリスト:
-
System.Collections.Immutable.ImmutableArray<T>
-
System.Collections.Immutable.ImmutableDictionary<TKey,TValue>
-
System.Collections.Immutable.ImmutableHashSet<T>
-
System.Collections.Immutable.ImmutableList<T>
-
System.Collections.Immutable.ImmutableQueue<T>
-
System.Collections.Immutable.ImmutableSortedDictionary<TKey,TValue>
-
System.Collections.Immutable.ImmutableSortedSet<T>
-
System.Collections.Immutable.ImmutableStack<T>