Итераторы и LINQ.

Содержание

Слайд 2

Интерфейс IEnumerable и IEnumerator Любая коллекция реализует интерфейс IEnumerable. public interface

Интерфейс 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 не связан с какой-то коллекцией.

Слайд 3

Ключевое слово yield Главным в интерфейсе IEnumerator является метод MoveNext(), поэтому

Ключевое слово yield

Главным в интерфейсе IEnumerator является метод MoveNext(), поэтому итератор

можно объявить, запрограммировав одну эту функцию, остальное компилятор доделает сам.
Для этого в С# существует слово yield.

IEnumerator M() {
yield return 1;
yield return 2;
yield return 3;
}

IEnumerable M() {
yield return 1;
yield return 2;
yield return 3;
}

for (var iter = M(); iter.MoveNext(); )
Console.WriteLine(iter.Current);

foreach (var i in M())
Console.WriteLine(i);

Более практично возвращать не IEnumerator а IEnumerable.

Здесь компилятор реализует сразу два интерфейса – IEnumerator и IEnumerable.

Слайд 4

Примеры итераторов Итератор может сам вырабатывать последовательность значений. Итератор может основываться

Примеры итераторов

Итератор может сам вырабатывать последовательность значений.

Итератор может основываться на входной

последовательности.

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;
}

...или на нескольких входных последовательностях.

Слайд 5

Задание 0 Реализовать итератор, который позволит проходить любой целый массив в

Задание 0

Реализовать итератор, который позволит проходить любой целый массив в обратном

порядке.
Реализовать обобщенный итератор, который позволит проходить любой массив в обратном порядке.
Сделать обобщенный итератор, который обходит двумерный массив по столбцам.
Объявить итератор, который получает один целочисленный массив в качестве аргумента и вырабатывает все уникальные значения из входного массива. { 2, 3, 4, 3 ,3, 1 ,2 } => {2, 3, 4, 1}
Объявить итератор, который получает два целочисленных массива в качестве аргументов и вырабатывает только те значения, которые есть в первом массиве и в то же время отсутствуют во втором массиве. {1, 2, 3, 5, 6}, {4, 5, 1 } => {2, 3, 6}
Слайд 6

Что такое LINQ to Objects Итератор IEnumerable можно преобразовать в другой

Что такое LINQ to Objects

Итератор IEnumerable можно преобразовать в другой итератор,

в число, в объект, в массив, в коллекцию, во что угодно.
Набор готовых методов-преобразователей, который содержится в статическом классе System.Linq.Enumerable, и составляет основное содержание LINQ to Objects.
Большинство преобразователей имеют форму методов, расширяющих тип 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

Выражения запросов using System; using System.Linq; using System.Collections.Generic; class Program {

Выражения запросов

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

Верхний уровень LINQ составляют выражения запросов.

Слайд 8

Компиляция выражений var query = from name in names where name.Length

Компиляция выражений

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());

Компилятор переводит выражения запроса в цепочку вызовов расширяющих методов.
Аргументами методов являются лямбда-выражения.

Слайд 9

Делегаты Func Методы LINQ, принимающие делегаты в качестве параметров, должны быть

Делегаты Func

Методы LINQ, принимающие делегаты в качестве параметров, должны быть объявлены

с указанием типа параметров-делегатов.
public static IEnumerable Where
(this IEnumerable source, Func predicate);
Для указания типа параметров используется обобщенный делегат Func,
объявленный в пространстве имен System.
public delegate TResult Func(T arg);
Делегат перегружен и может принимать от 0 до 9 входных параметров-типов (T1, T2, T3,…).
Слайд 10

Запросы Where() и Select() Where() – фильтр Пример: отфильтровать все четные

Запросы 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 );

result = m.Where(a => a % 2 == 0).Select(a => a * 1000 );

Пример: цепочка вызовов.

Слайд 11

Запрос SelectMany() string[] input = { "SELECT", "FROM", "WHERE", "ORDER BY"

Запрос SelectMany()

string[] input = { "SELECT", "FROM", "WHERE", "ORDER BY"

};
var output = input.Select(s => s.ToCharArray());

var output = input.SelectMany(s => s.ToCharArray());

foreach (var array in output) {
foreach (var v in array) {
Console.Write(v + " ");
}
}

foreach (var v in output) {
Console.Write(v + " ");
}

Слайд 12

Упорядочение – OrderBy(), ThenBy() string[] input = { "Select", "Where", "OrderBy",

Упорядочение – OrderBy(), ThenBy()

string[] input = { "Select", "Where", "OrderBy",

"GroupBy" };
Так можно упорядочить строки по алфавиту:
var output = input.OrderBy(s => s);
по длине:
var output = input.OrderBy(s => s.Length);
по последней букве слова:
var output = input.OrderBy(s => s[s.Length - 1]);

var result = books.OrderBy(b => b.Author).ThenBy(b => b.Title);

Пример. Упорядочим книги в первую очередь по фамилии автора, во вторую очередь – по названию.

После упорядочения тип итератора меняется.

Слайд 13

Группирование – запрос GroupBy() var result = input.GroupBy(s => s.Length); foreach

Группирование – запрос GroupBy()

var result = input.GroupBy(s => s.Length);

foreach (IGrouping

group in result) {
Console.WriteLine("Strings of length {0}", group.Key);
foreach (string value in group)
Console.WriteLine(" {0}", value);
}

Запрос GroupBy() может не только сгруппировать данные, но и выполнить их проекцию.
Выражение для проекции задается во втором параметре запроса.
var result = input.GroupBy(s => s.Length, s => s.ToUpper());

Пример: сгруппировать строки по длине.

string[] input = { "Select", "Where", "OrderBy", "GroupBy" };

Слайд 14

Задание 1 Имеется список книг – объектов класса Book: class Book

Задание 1

Имеется список книг – объектов класса Book:
class Book
{
public

string Name;
public int Year;
}
Выберите все книги, в названии которых есть слово LINQ, а год издания високосный.
Дана последовательность русских слов. Сколько букв алфавита понадобилось для написания этих слов?
Дан массив целых двузначных чисел. Упорядочить их по возрастанию старшего разряда, а затем по убыванию младшего, например, { 14, 12, 23, 20, 33, 32 }.
Дан массив книг books. Сколько книг написал каждый автор (распечатать в два столбца: Автор, Количество)?
Слайд 15

Агрегация – Agregate, Sum, Average, Max, Min Суммирование: int[] m =

Агрегация – Agregate, Sum, Average, Max, Min

Суммирование:
int[] m =

{ 2, 6, 4, 9, 1 };
int result = m.Sum(s => s);
Усреднение:
var result = m.Average(s => s);
Наибольший и наименьший элемент:
var result = m.Max(s => s);
var result = m.Min(s => s);

Обобщенный запрос на агрегацию

public static U Aggregate(this IEnumerable source,
U seed,
Func func)

var sum = m.Aggregate(0, (s, i) => s += i);

Параметр у всех запросов означает преобразование перед агрегированием
books.Max(s => s.Year)

Слайд 16

Соединение – Join Все элементы одной последовательности попарно сравниваются с элементами

Соединение – Join

Все элементы одной последовательности попарно сравниваются с элементами второй

последовательности, и для каждой пары проверяется условие соединения.

Параметры запроса:
вторая последовательность,
функция для построения первого ключа,
функция для построения второго ключа,
функция для образования проекции.

string[] boys = { "Alex", "Bob", "Charley", "Dick"};
string[] girls = { "Caroline", "Barbara", "Ann", "Adel"};
Образуем пары с именами на одну букву.
var result = boys.Join(girls,
x => x[0],
y => y[0],
(x, y) => new {Boy = x, Girl = y}
);

foreach (var r in result)
Console.WriteLine("{0} + {1}", r.Boy, r.Girl);

Alex + Ann
Alex + Adel
Bob + Barbara
Charley + Caroline

Слайд 17

Групповое соединение - GroupJoin Пару составляют элемент первой последовательности и все

Групповое соединение - GroupJoin

Пару составляют элемент первой последовательности и все соответствующие

ему элементы второй последовательности.

string[] boys = { "Alex", "Bob", "Charley", "Dick"};
string[] girls = { "Caroline", "Barbara", "Ann", "Adel"};
var result = boys.GroupJoin(girls, x => x[0], y => y[0],
(x, matches) => new { Boy = x, Girls = matches });

foreach (var r in result) {
Console.WriteLine(r.Boy);
foreach (var g in r.Girls) {
Console.WriteLine(" " + g);
}
}

Alex
Ann
Adel
Bob
Barbara
Charley
Caroline
Dick

Образуем групповое соединение имен на одну букву.

Слайд 18

Задание 2 1. Выразите через запрос Agregate() запросы Count(), Max() и

Задание 2

1. Выразите через запрос Agregate() запросы Count(), Max() и Average().
2.

Дан массив книг books. Сколько книг написал каждый автор (получить последовательность пар: автор – число книг)?
3. Дан массив чисел. Составьте все пары …
а) из всевозможных чисел;
б) из чисел, не равных между собой;
в) исключив пару (a, b), если пара (b, a) уже есть в результате.
Слайд 19

Остальные запросы

Остальные запросы

Слайд 20

Разбиение: Take, Skip, TakeWhile, SkipWhile int[] m = { 3, 1,

Разбиение: Take, Skip, TakeWhile, SkipWhile

int[] m = { 3, 1, 8,

11, 9 };
var result = m.Take(2);

var result = m.TakeWhile(a => a < 10);

Запросы Skip и SkipWhile – противоположность запросам Take и TakeWhile

негатив

позитив

Слайд 21

Union, Intersect, Except, Distinct A.Union(B) A.Intersect(B) A.Except(B) int[] m = {

Union, Intersect, Except, Distinct

A.Union(B)

A.Intersect(B)

A.Except(B)

int[] m =

{ 2, 1, 3, 2 }, m1 = { 2, 12, 13, 12};
var result = m.Except(m1); // result = { 1, 3}

Distinct() устраняет дубликаты.
int[] m = {2, 3, 2, 2, 1, 3};
var result = m.Distinct();

class MyComparer: EqualityComparer {
public override bool Equals(int x, int y)
{ return x % 10 == y % 10; }
public override int GetHashCode(int obj)
{ return obj % 10; }
}

int[] m = { 12, 13, 12, 2, 1, 3 };
var result = m.Distinct(new MyComparer());
// result = { 2, 3, 1}

Дубликаты игнорируются

Слайд 22

Отдельные элементы: First, Last, ElementAt, Single У всех запросов есть условный

Отдельные элементы: First, Last, ElementAt, Single

У всех запросов есть условный

вариант:

First

Last

Single

OrDefault

ElementAt

OrDefault

OrDefault

OrDefault

Слайд 23

Генераторы: Repeat, Range, Empty Repeat() – последовательность из повторений. IEnumerable strings

Генераторы: Repeat, Range, Empty

Repeat() – последовательность из повторений.
IEnumerable strings =

Enumerable.Repeat("GL", 10);
Range() – отрезок целых чисел.
IEnumerable squares = Enumerable.Range(1, 10);
Empty() – пустая последовательность.
IEnumerable empty = Enumerable.Empty();
Пустую последовательность можно использовать как начальное значение агрегата в запросе Agregate().
Слайд 24

ToArray, ToList, ToDictionary, ToLookup, AsEnumerable Запросы ToArray() и ToList() просто возвращают

ToArray, ToList, ToDictionary, ToLookup, AsEnumerable

Запросы ToArray() и ToList() просто возвращают

массив или коллекцию, составленную из элементов исходной последовательности.
Запрос ToDictionary() преобразует последовательность в словарь – коллекцию пар (ключ, значение). При этом можно задать функцию выбора ключа, функцию выбора значения и функцию-компаратор, которая принимает решение об эквивалентности ключей.
Запрос ToLookup() преобразует последовательность в мультисловарь. От словаря он отличается тем, что одному ключу может соответствовать любое количество значений, в том числе и ни одного.
Метод AsEnumerable() позволяет привести тип, наследующий интерфейс IEnumerable (например, IQueryable), к типу IEnumerable. Это может понадобиться для того, чтобы компилятор выбрал реализацию запросов из класса Enumerable, а не из класса Queryable.
Слайд 25

Преобразования элементов: OfType, Cast static void Main() { Book[] books =

Преобразования элементов: OfType, Cast

static void Main() {
Book[] books

= {
new Book {Author = "Албахари", Title = "LINQ: карманный справочник", Year = 2009 },
new Book {Author = "Раттц", Title = "LINQ: язык запросов", Year = 2008 },
new Book {Author = "Kimmel", Title = "LINQ Unleashed", Year = 2008 }
};
ArrayList bookList = new ArrayList(books);
var result1 = bookList.OfType().Take(2);
var result2 = books.OfType().Take(2);
var result3 = books.Take(2);
}

Запрос OfType() выбирает из входной последовательности элементы, которые могут быть приведены к типу TResult. В результате на выходе получается последовательность типа IEnumerable.
Запрос OfType() применим не только к IEnumerable, но и к IEnumerable. Благодаря этому LINQ способен работать с коллекциями старого типа: ArrayList, SortedList, Hashtable.

Запрос Cast() отличается от запроса OfType тем, что пытается привести к типу TResult все элементы входной последовательности. Если с каким-то элементом это не получается, выбрасывается исключение InvalidCastException (запрос OfType исключения не выбрасывает, он просто пропускает неподходящий элемент)

Слайд 26

Кванторы: All, Any, Contains Эти запросы возвращают логическое значение и называются

Кванторы: All, Any, Contains

Эти запросы возвращают логическое значение и называются кванторами,

как аналогичные функции на множествах.
Квантор All возвращает true, если все элементы последовательности удовлетворяют заданному условию. Условие задается при помощи функции – параметра запроса.
Квантор Any возвращает true, если в последовательности есть хотя бы один элемент, удовлетворяющий заданному условию.
Квантор Contains возвращает true, если последовательность содержит заданный элемент. При этом можно задать собственное правило сравнения в виде объекта-компаратора.