ИТЕРАТОРЫ И LINQ Лекция 1
Интерфейс IEnumerable и IEnumerator Любая коллекция реализует интерфейс IEnumerable. public interface IEnumerable : IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerator : IDisposable, IEnumerator { T Current { get; } bool MoveNext(); // false, когда дальше ехать некуда void Reset(); } Объект, возвращаемый методом GetEnumerator(), называется итератор. Итератор реализует интерфейс IEnumerator : Замечание. Интерфейс IEnumerator не обязательно подразумевает коллекцию. 2
Ключевое слово yield Главным в интерфейсе IEnumerator является метод MoveNext(), поэтому итератор можно объявить, запрограммировав только его, остальное компилятор доделает сам. Для этого в С# существует слово yield. IEnumerator Iter() { yield return 1; yield return 2; yield return 3; } IEnumerable Iter() { yield return 1; yield return 2; yield return 3; } for (var iter = Iter(); iter.MoveNext(); ) Console.WriteLine(iter.Current); foreach (var i in Iter()) Console.WriteLine(i); Если возвратить не IEnumerator а IEnumerable, то компилятор создаст боле удобный в использовании объект. 3
Примеры итераторов Итератор может сам вырабатывать последовательность значений. Итератор может основываться на входной последовательности. static IEnumerable Range(int a, int b) { for (int i = a; i < b; i++) yield return i; } static IEnumerable Filter(IEnumerable it) { foreach (var x in it) if (x % 2 == 0) yield return x; } 4
Примеры итераторов static IEnumerable Decart(IEnumerable a, IEnumerable b) { foreach (var x in a) foreach (var y in b) yield return new Point(x, y); } Итератор может основываться на двух входных последовательностях. 5 static IEnumerable Maximes(params IEnumerable [] xs) { foreach (var x in xs) yield return Max(x); } или на любом количестве входных последовательностей.
Что такое LINQ to Objects Как показывают примеры, одни итераторы можно преобразовывать в другие итераторы, в числа, в массивы, в коллекцию, во что угодно. Набор готовых методов-преобразователей, который содержится в статическом классе System.Linq.Enumerable, и составляет основное содержание LINQ to Objects. 6
Цепочечная композиция Большинство преобразователей имеют форму методов, расширяющих тип IEnumerable, например, public static int Count (this IEnumerable source); Композицию таких методов можно записать так. int n = Enumerable.Range(1, 10).Where(x => x % 2 == 0).Count(); Для сравнения – композиция, записанная обычным способом. Enumerable.Count(Enumerable.Where(Enumerable.Range(1, 10), x => x % 2 == 0)); 7
Выражения запросов От цепочечного выражения 8 int n = Enumerable.Range(1, 10).Where(x => x % 2 == 0).Count(); var v = from x in Enumerable.Range(1, 10) where x % 2 == 0 select x; int n = v.Count(); один шаг до выражения запроса.
Пример выражения запроса using System; using System.Linq; using System.Collections.Generic; class Program { static void Main() { string[] names = { "Burke", "Connor", "Frank", "Everett", "Albert", "George", "Harris", "David" }; var query = from name in names where name.Length == 5 orderby name select name.ToUpper(); foreach (string item in query) Console.WriteLine(item); } BURKE DAVID FRANK 9 Выбрать из массива имен пятибуквенные имена и перевести их в верхний регистр.
Компиляция запросов var query = from name in names where name.Length == 5 orderby name select name.ToUpper(); var query = names.Where (name => name.Length == 5).OrderBy (name => name).Select (name => name.ToUpper()); Компилятор переводит выражения запроса в цепочку вызовов расширяющих методов. 10
Делегаты Func Чтобы объявить метод LINQ, с параметром-делегатом, нужно указать тип параметра. public static IEnumerable Where (this IEnumerable source, Func predicate); Для указания типа параметров используются обобщенный делегат Func, объявленный в пространстве имен System. public delegate TResult Func (T arg); Делегат перегружен и может принимать от 0 до 16 входных параметров-типов (T1, T2, T3,…). 11
Запросы Where() и Select() Where() – фильтр Пример: оставить только четные числа IEnumerable result = m.Where(a => a % 2 == 0); Пример: оставить только числа, стоящие на четных местах IEnumerable result = m.Where((a, i) => i % 2 == 0); Select() – проекция Пример: увеличить все числа в 1000 раз IEnumerable result = m.Select(a => a * 1000 ); var result = m.Where(a => a % 2 == 0).Select(a => a * 1000 ); И оба вместе: 12
Самостоятельно 1.Реализовать итератор, который позволит проходить любой целый массив в обратном порядке. 2.Реализовать обобщенный итератор, который позволит проходить любой массив в обратном порядке. 3.Сделать обобщенный итератор, который обходит двумерный массив по столбцам. 4.Объявить итератор, который получает один целочисленный массив в качестве аргумента и вырабатывает все уникальные значения из входного массива. { 2, 3, 4, 3,3, 1,2 } => {2, 3, 4, 1} 5.Объявить итератор, который получает два целочисленных массива в качестве аргументов и вырабатывает только те значения, которые есть в первом массиве и в то же время отсутствуют во втором массиве. {1, 2, 3, 5, 6}, {4, 5, 1 } => {2, 3, 6} 13