Синтаксис кортежей в C # смехотворно громоздкий, поэтому объявлять кортежи больно. И у него нет сопоставления с образцом, поэтому их также сложно использовать.
Но иногда вам просто нужна специальная группировка объектов без создания для нее класса. Например, скажем, я хотел объединить список, но я хотел два значения вместо одного:
// sum and sum of squares at the same time
var x =
Enumerable.Range(1, 100)
.Aggregate((acc, x) => Tuple.Create(acc.Item1 + x, acc.Item2 + x * x));
Вместо объединения набора значений в один результат, давайте расширим один результат в набор значений. Самый простой способ написать эту функцию:
static IEnumerable<T> Unfold<T, State>(State seed, Func<State, Tuple<T, State>> f)
{
Tuple<T, State> res;
while ((res = f(seed)) != null)
{
yield return res.Item1;
seed = res.Item2;
}
}
f
преобразует некоторое состояние в кортеж. Мы возвращаем первое значение из кортежа и устанавливаем наше новое состояние на второе значение. Это позволяет нам сохранять состояние на протяжении всего вычисления.
Вы используете его так:
// return 0, 2, 3, 6, 8
var evens =
Unfold(0, state => state < 10 ? Tuple.Create(state, state + 2) : null)
.ToList();
// returns 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
var fibs =
Unfold(Tuple.Create(0, 1), state => Tuple.Create(state.Item1, Tuple.Create(state.Item2, state.Item1 + state.Item2)))
.Take(10).ToList();
evens
довольно прост, но fibs
немного умнее. Его state
на самом деле является кортежем, который содержит fib (n-2) и fib (n-1) соответственно.