Главная » C# » Вычисление функций – функциональное программирование

0

В то время как неизменяемые типы и чистые функции, несомненно, являются пезными, они требуют определенного образа программирования. В некоторых слаях создание неизменяемого объекта может быть невозможным. В таком случае можно написать код с побочными эффектами и надеяться  на лучшее.  Но имеется и другое решение, основанное на том, каким образом вычисляются функции.

Не вдаваясь в подробности  теории вычисления функций, в основном, мы хотим получить такую ситуацию, когда, независимо от способа вычисления выражения, результат всегда получается одинаковый. Добиться этого в императивном языке — задача не из легких, если только в императивном языке нет встроенных методик, предназначенных для этой цели. Например, одним из  методов,  который  может быть встроен в императивный язык, являются производные функции.

Сначала посмотрим на код, который имеет побочные эффекты:

class ClassWithSideEffects { public ClassWithSideEffects() {

_islnitialized = false;

bool _islnitialized;

string _value;

public void Initialize(string value) {

_islnitialized = true;

_value = value;

}

public string GetMeAValue() {

if (Reinitialized) {

return „value;

}

throw new NullReferenceExceptionC’Not initialized");

}

}

Жирным шрифтом выделен код, требующий  исправлений.  Для  этого  будет  примен подход отложенного вычисления (lazy evaluation), использующий производную функцию.   При   отложенном    вычислении   результаты    вычисляются   тогда,    когда в них возникает надобность, а до тех пор состояние вызова сохраняется. Далее продится  полный  код  решения:

delegate void LazyInitialization();

class ClassWithoutSideEffects { string _value;

public ClassWithoutSideEffects(Func<string> remotelnitialize) { Initialize = delegate() {

this._value = remotelnitialize(); Initialize = delegate() { };

} ;

}

protected LazyInitialization Initialize; public string GetMeAValue() {

Initialize(); return _value;

}

}

Первым делом,  обратите  внимание на объявление  initialize:

protected LazyInitialization Initialize;

Это  хитроумное  объявление  имеет  вид  и  создает  впечатление  первоначального  мода   initialize(),  но  без   параметра.  Объявление   initialize подобно   члену  даых.  Хитроумность  же  заключается  в  том,  что  член  данных  является  объявлением делегата    и    поэтому    ведет    себя    подобно    настоящему    объявленному    методу

Initialize().

Идея в основе отложенного объявления initialize состоит в том, чтобы вызывать его, когда в нем возникнет надобность, но не раньше. Это означает, что нет надоости объявлять наперед, каким должно быть  значение _value, но нужно иметь возможность получить это значение, когда в нем возникнет необходимость. Это можно сравнить с ситуацией, когда мастер объясняет ученику, как выполнять оеделенную работу: "Следуй инструкциям, а если возникнут затруднения, то пози меня". Часть "позови меня" является отложенным вычислением, которое позвяет в дальнейшем получить информацию в случае необходимости.

Этим дальнейшим получением информации является производная  функция,  котая вызывается в какое-то более позднее время. Инициализация состояния должна выполняться только один раз и далее не повторяться. Таким образом, отложенное вычисление должно иметь возможность обновить само себя позже. Это достигается объявлением анонимного метода в конструкторе класса classwithoutsideEf fects: public QassWithoutSideEffects(Func<string> remotelnitialize)  {

Initialize = delegate() { this._value = remotelnitialize() ; Initialize = delegate() { };

} ;

}

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

Переназначение  initialize—  довольно  хитроумная  операция.  В  результате  ее выполнения метод члена данных initialize можно вызывать сколько угодно раз, но  инициализация  состояния  будет  выполнена  только  один  раз.  Таким  образом, в методе GetMeAValue () операция принятия решения заменяется вызовом initialize: public string GetMeAValue() {

Initialize(); return _value;

}

В новой реализации метода GetMeAValue () всегда вызывается initialize и всегда возвращается значение. А т. к. при создании экземпляра типа нам нужно опредить отложенную функцию инициализации, то мы имеем код, свободный от почных эффектов, насколько это возможно.

Чтобы завершить данное решение, в следующем коде используется новый объяенный класс, не имеющий побочных эффектов:

Func<string, Func<string>> lazyString = (stringToRetrieve) => О => { return stringToRetrieve;

} ;

ClassWithoutSideEffects els = new

ClassWithoutSideEffects(lazyString("hello")); Console.WriteLine("Value (" + els.GetMeAValue() + ")");

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

Производная функция передается конструктору класса  classwithoutsideEffects, после чего метод GetMeAValue ()  вызовет  отложенную  операцию  создания  экземяра  и  возвратит  инициализированное  состояние.

Источник: Гросс  К. С# 2008:  Пер. с англ. — СПб.:  БХВ-Петербург, 2009. — 576 е.:  ил. — (Самоучитель)

По теме:

  • Комментарии