🐕
Functional Programming in C# まとめ 12章
11章のまとめは、こちら
12. Stateful programs and stateful computations
この章では以下の内容を説明する。
- 何がプログラムをステートフルにするのか?
- 状態を変更することなくステートフルなプログラムを書く
- ランダムな構造を生成する
- ステートフルな計算を合成する
12.1. Programs that manage state
using Rates = ImmutableDictionary<string, decimal>;
static (decimal, Rates) GetRate
(Func<string, decimal> getRate, string ccyPair, Rates cache)
{
if(cache.ContainsKey(ccyPair))
return (cache[ccyPair], cache);
var rate = getRate(ccyPair);
return (rate, cache.Add(ccyPair, rate));
}
ステートフル計算は、状態を取り、新しい状態を返す関数。状態遷移とも呼ぶ。
このようにすることで、グローバル変数やその状態変更をせずに、ステートフルな(状態に依存した)計算を行うことができる。
12.2. A language for generationg random data
乱数生成器(Random)は、本当の乱数を生成しているわけではなく、乱数に見える値を生成しているだけである。そのため、シードを固定すると、常に同じ結果が得られるようになる。Nextを呼ぶ度に出力される値が変わるのは、乱数生成器の内部状態が変化しているからである。つまり、Nextには副作用がある。
ここで、副作用がない乱数生成器をつくりたい。
public delegate(T Value, int Seed) Generator<T>(int seed);
ここで、intの乱数生成器を作ってみる。※実装は一例
public static Generator<int> NextInt = (seed) =>
{
seed ^= seed >> 13;
seed ^= seed >> 18;
int result = seed & 0x7fffffff;
return (result, result);
};
これを基にして、boolの乱数生成器を考える。
public static Generator<bool> NextBool = (seed) =>
{
var (i, newSeed) = NextInt(seed);
return (i % 2 == 0, newSeed);
};
これは、NextIntに関数を適用しただけなので、をMapを使って一般化する。
public static Generator<R> Map<T, R>
(this Generator<T> gen, Func<T, R> f)
=> seed =>
{
var (t, newSeed) = gen(seed);
return (f(t), newSeed);
};
この一般化を利用するとboolの乱数生成器は以下のように書き直せる
public static Generator<bool> NextBool =>
from i in NextInt
select i % 2 == 0;
毎回、関数のコンテナではよくわからなくなってしまうのですが、
- i:NextIntにseedを与えたときに得られる結果の乱数
- select:Map関数なので、結果は、seedを与えると乱数と新しいseedを出力する関数になる
ということで、新しい乱数生成器が得られます。
次に、intのペアを生成する場合を考える。
public static Generator<(int, int)> PairOfInts = (seed0) =>
{
var (a, seedl) = NextInt(seed0);
var (b, seed2) = NextInt(seed1);
return ((a, b), seed2);
}
状態(seed)の扱いが煩雑。以下のように書きたい。
public static Generator<(int, int)> PairOfInts =>
{
from a in NextInt
from b in NextInt
select (a, b);
}
BindとSelectManyを定義することで実現する。
public static Generator<R> Bind<T, R>
(this Generator<T> gen, Func<T, Generator<R>> f)
=> seed0 =>
{
var (t, seed1) = gen(seed0);
return f(t)(seed1)
}
12.3. A general pattern for stateful computations
以下は、より一般的なステートフル計算のデリゲートである:
delegate(T Value, S State) StatefulComputation<S, T>(S state);
S -> (T, S)
※FPの用語では、状態monadと呼ばれるもの。
Discussion