Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Vse_shpory_po_OOP_polnye.doc
Скачиваний:
6
Добавлен:
20.09.2019
Размер:
295.94 Кб
Скачать

20. Захват переменных в лямбда-выражениях. Рекурсивный вызов в лямбда-выражениях. Анонимная рекурсия.

Замыкания (захват переменной)

Анонимные методы могут захватывать контекст своего лексического окру­жения. Многие называют этот феномен захватом переменной. На языке функцио­нального программирования это также известно как замыкание (closure). Рассмотрим простой пример замыкания в действии:

public class Closures

{

static void Main ()

{

int delta = 1;

Func<int, int> func = (x) => x + delta;

int currentVal = 0;

for( int i = 0; i < 10; ++i )

{

currentVal = func( currentVal );

Console.WriteLine( carrentVal );

}

}

}

Переменная delta и делегат func формируют замыкание. Тело выражения ссы­лается на delta и потому должно иметь доступ к ней при последующем выполне­нии. Чтобы обеспечить это, компилятор "захватывает" переменную для делегата. "За кулисами" же происходит вот что: тело делегата содержит ссылку на действи­тельную переменную delta. Более того, поскольку захваченная переменная дос­тупна как делегату, так и контексту, содержащему лямбда-выражение, это означа­ет, что захваченная переменная может быть изменена вне контекста и вне связи с делегатом.

Одной из основ функционального программирования является то, что сама функция трактуется как первоклассный объект, которым можно манипулировать и оперировать, а также вызывать. Функции можно использовать в качестве строительных блоков для создания новых функций. Рассмотрим следующие два лямбда-выражения:

х => x*3;

x => x + 3.1415;

Можно создать метод для комбинирования таких лямбда-выражений с целью создания составного лямбда-выражения:

public class Compound

{

static Func<T,S> Chain<T, R, S> (Func<T,R> func1, Func<T,R> func2)

{

return x => func2 (func1(x));

}

static void Main ()

{

Func<int, double> func = Chain ((int x) => x*3, (int x) => x + 3.1415);

Console.WriteLine (func (2));

}

}

Метод Chain() принимает два делегата и производит третий делегат, комби­нируя первые два. В методе Main он используется для про­изводства составного выражения. Делегат, который мы получаем после вызова Chain(), эквивалентен делегату, который мы получаем при преобразовании сле­дующего лямбда-выражения в делегат:

х => (х * 3) + 3.1415

Рассмотрим другие способы создания произ­водных функций.

Построим операцию для вычисления n-го числа Фибоначчи:

public class Proof

{

static void Main ()

{

Func<int, int> fib = null;

fib = (x) => x > 1 ? fib(x-l) + fib(x-2) : x;

for (int i = 30; i < 40; ++i ) (

{ Console.WriteLine( fib(i) );}

}

}

Первое, что бросается в глаза при взгляде на этот код — формирование проце­дуры Фибоначчи, т.е. делегата fib. Он формирует замыкание на самом себе! Это определенно форма рекурсии, и она делает то, что надо. А именно, строит число Фибоначчи на основании предыдущих чисел.

Анонимная рекурсия. Рассмотрим замыка­ние, которое можно использовать для вычисления факториала числа:

Func<int, int> fact = null;

fact = (x) => x>l ? x* fact(x-l) : 1;

Этот код работает, потому что fact формирует замыкание на самом себе и так­же вызывает себя. То есть вторая строка, где fact присваивается лямбда-выраже­ние для вычисления факториала, захватывает сам делегат fact. Невзирая на то, что замыкание захватывает переменную для использования внутри анонимного метода, который реализован здесь в виде лям­бда-выражения, захваченная переменная остается доступной для изменения вне контекста захватывающего анонимного метода или лямбда-выражения.

Существует несколько способов решить эту проблему, но наиболее типичный заключается в использовании анонимной рекурсии.

Суть в том, что вместо рекурсии, полагающейся на захваченную переменную, являющуюся делегатом, мы передаем делегат рекурсии в качестве параметра. Теперь захвачен­ная переменная находится в стеке, поскольку передается каждой рекурсии выражения. Однако, поскольку она в стеке, опасность модификации извне меха­низма рекурсии исключается.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]