Обчислення функцій – функціональне програмування

У той час як незмінні типи і чисті функції, безсумнівно, є пезнимі, вони вимагають певного способу програмування У деяких слаях створення незмінного обєкта може бути неможливим У такому випадку можна написати код з побічними ефектами і сподіватися на краще Але є і інше рішення, засноване на тому, яким чином обчислюються функції

Не вдаючись у подробиці теорії обчислення функцій, в основному, ми хочемо отримати таку ситуацію, коли, незалежно від способу обчислення виразу, результат завжди виходить однаковий Домогтися цього в імперативному мовою – завдання не з легких, якщо тільки в імперативному мові немає вбудованих методик, призначених для цієї мети Наприклад, одним з методів, який може бути вбудований в імперативний мову, є похідні функції

Спочатку подивимося на код, який має побічні ефекти:

class ClassWithSideEffects { public ClassWithSideEffects() {

_islnitialized = false

&gt&nbsp

bool _islnitialized

string _value

public void Initialize(string value) {

_islnitialized = true

_value = value

}

public string GetMeAValue() {

if (Reinitialized) {

return „value

}

throw new NullReferenceExceptionCNot initialized&quot)

}

}

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

delegate void LazyInitialization()

class ClassWithoutSideEffects { string _value

public ClassWithoutSideEffects(Func&ltstring&gt 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 remotelnitialize)  {

Initialize = delegate() { this_value = remotelnitialize() Initialize = delegate() { }

}

}

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

Перепризначення initialize-досить хитромудра операція У результаті її виконання метод члена даних initialize можна викликати скільки завгодно разів, але ініціалізація стану буде виконана тільки один раз Таким чином, в методі GetMeAValue () операція прийняття рішення замінюється викликом initialize: public string GetMeAValue () {

Initialize() return _value

}

У новій реалізації методу GetMeAValue () завжди викликається initialize і завжди повертається значення А оскільки при створенні екземпляра типу нам потрібно визна відкладену функцію ініціалізації, то ми маємо код, вільний від почном ефектів, наскільки це можливо

Щоб завершити дане рішення, в наступному коді використовується новий обяенний клас, який не має побічних ефектів:

Func > lazyString = (stringToRetrieve) => О => {Return stringToRetrieve

}

ClassWithoutSideEffects els = new

ClassWithoutSideEffects(lazyString(&quothello&quot)) ConsoleWriteLine(&quotValue (&quot + elsGetMeAValue() + &quot)&quot)

Оголошення lazyString є похідною функцією, яка використовується для тимчасового зберігання посилання на строковий буфер Але похідну функцію МГО використовувати не тільки для цього Вона може містити посилання на підключення до бази даних, які необхідно створити

Похідна функція передається конструктору класу classwithoutsideEffects, після чого метод GetMeAValue () викличе відкладену операцію створення екземяра і поверне ініціалізованих стан

Джерело: Гросс К С # 2008: Пер з англ – СПб: БХВ-Петербург, 2009 – 576 е: ил – (Самовчитель)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*