Введення в функціональне програмування для. NET-розробників, HTML, XML, DHTML, Інтернет-технології, статті

Напевно останнім часом ви хоч щось чули про F # – новітньому поповнення в сімействі мов для Microsoft Visual Studio. Для вивчення F # є багато вагомих причин: чіткий синтаксис, потужні засоби роботи з безліччю потоків і здатність до взаємодії з іншими мовами, сумісними з Microsoft. NET Framework. Однак F # включає кілька нових важливих концепцій, у які потрібно вникнути, перш ніж користуватися ними.


Короткий екскурс – непоганий спосіб приступити до освоєння іншого об’єктно-орієнтованої мови або навіть динамічного мови на зразок Ruby або Python. Таке можливо тому, що вам вже відома велика частина словникового запасу подібних мов і ви просто вивчаєте новий синтаксис. Але F # стоїть тут осібно. F # – це мова функціонального програмування, і його словник відрізняється куди більше, ніж ви могли б очікувати. Більш того, функціональні мови традиційно застосовувалися в академічних колах, тому визначення абсолютно нових для вас термінів можуть виявитися складними в розумінні.


На щастя, F # проектували зовсім не як академічний мову. Його синтаксис дозволяє за допомогою методик функціонального програмування вирішувати завдання новими і більш ефективними способами, в той же час підтримуючи об’єктно-орієнтовані і імперативні стилі, до яких ви звикли як. NET-розробник. На відміну від інших. NET-мов структура F # на основі безлічі парадигм означає, що ви вільні у виборі потрібного для конкретної задачі стилю програмування. Функціональне програмування в F # полягає в написанні чіткого та ефективного коду для вирішення проблем реального програмного забезпечення. Воно зав’язано на такі методики, як застосування функцій більш високого порядку (higher order functions) і композиція функцій (function composition) для створення ефективних і простих у розумінні поводжень. І воно ж полягає в тому, щоб ваш код було легше сприймати, тестувати і распараллелівать, видаляючи приховані складності.


Але, щоб скористатися перевагами всіх цих фантастичних коштів F #, потрібно розібратися в основах. У цій статті я поясню базові концепції, використовуючи словник, до якого ви вже звикли в. NET. Я також покажу деякі прийоми функціонального програмування, які ви зможете застосовувати в існуючому коді, і деякі способи, при яких ви вже програмували функціонально. До кінця статті ви зрозумієте функціональне програмування в достатній мірі, щоб почати роботу з F # в Visual Studio 2010.


Основи функціонального програмування


Більшості. NET-розробників легше зрозуміти функціональне програмування, відштовхуючись від зворотного – від того, чим воно не является.Імператівное програмування вважається стилем, прямо протилежним функціональному. Це також стиль, з яким ви, мабуть, знайомі найкраще, оскільки більшість мейнстрімових мов програмування є імперативними.


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

int number = 0;
number++;

Очевидно, що тут значення змінної збільшується на одиницю. Нічого особливого, але погляньте на інший спосіб вирішення тієї ж задачі:

const int number = 0;
const int result = number + 1;

Значення number як і раніше збільшується на одиницю, але воно не модифікується за місцем. Результат зберігається як інша константа, тому що компілятор не дозволяє змінювати значення константи. Константи незмінні, тому що їх значення не можна модифікувати після визначення. І навпаки, мінлива number в першому прикладі була змінною, так як ви могли модифікувати її значення. Ці два підходи ілюструють одну з фундаментальних відмінностей між імперативним і функціональним програмуванням. У імперативний програмуванні робиться наголос на використання змінних змінних, тоді як у функціональному – На застосування незмінних значень.


Більшість. NET-розробників сказало б, що number і result в попередніх прикладах є змінними, але як “функціональний” програміст ви повинні акуратніше добирати слова. Зрештою, одна лише ідея постійної змінної може просто запитати в кращому випадку. Натомість у функціональному програмуванні кажуть, що number і result є значеннями. Термін “змінна” резервується за об’єктами, які можна змінювати. Зауважте, що ці терміни – не ексклюзив функціонального програмування, але вони набагато важливіше при програмуванні в функціональному стилі.


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


Якщо на даному етапі функціональне програмування викликає у вас скепсис, не хвилюйтеся. Це природно. Більшість “імперативних” програмістів навчали тому, що з незмінними значеннями не можна зробити нічого корисного. Але розглянемо такий приклад:

string stringValue = “world!”;
string result = stringValue.Insert(0, “hello “);

Функція Insert формує рядок “hello world!”, Але ви знаєте, що Insert не модифікує вихідну рядок. А все тому, що в. NET рядки незмінні. Проектувальники. NET Framework використовували тут функціональний підхід, так як він спрощує написання більш якісного коду, що працює з рядками. Оскільки рядки – один з найбільш широко застосовуваних типів даних в. NET Framework (поряд з іншими базовими типами кшталт цілих, DateTimes та іншими), всі шанси за те, що насправді ви використовуєте ширше функціональне програмування, ніж вам це здається.


Приступаючи до роботи з F #


F # поставляється з Visual Studio 2010, і останню його версію можна знайти за посиланням msdn.microsoft.com / vstudio. Якщо ви використовуєте Visual Studio 2008, то скачайте надбудову для F # з F # Developer Center за посиланням msdn.microsoft.com / fsharp, де ви також знайдете інструкції по установці Mono.


F # додає в Visual Studio нове вікно – F # Interactive, яке дозволяє інтерактивно виконувати код на F #. Ви можете вважати його більш потужною версією вікна Immediate, доступною навіть у тому випадку, якщо ви не переключалися в режим налагодження. Якщо ви знаєте Ruby або Python, то помітите, що F # Interactive виконує цикл “читання-оцінка-вивід” (Read-Evaluate-Print Loop, REPL), який є корисним засобом для освоєння F # і можливістю швидко поекспериментувати з кодом.


Я буду використовувати F # Interactive, щоб показати, що відбувається при компіляції та виконання коду. Якщо ви виділите якийсь код в Visual Studio і натисните Alt + Enter, то відправите його в F # Interactive. Візьмемо простий приклад на F #:

let number = 0
let result = number + 1

Запустивши цей код в F # Interactive, ви отримаєте наступне:

val number : int = 0
val result : int = 1

Ймовірно, ви вже здогадалися за ключовим словом val, що number і result є незмінними значеннями. Ви можете побачити це, використавши оператор присвоювання (<-) в F #:

> number <- 15;;

number <- 15;;
^^^^^^^^^^^^

stdin(3,1): error FS0027: This value is not mutable
>


Оскільки ви тепер знаєте, що функціональне програмування засноване на незмінності, ця помилка має бути вам зрозумілою. Ключове слово let використовується для створення незмінних прив’язок між іменами та значеннями. У термінології C # те ж саме можна було сказати так: за замовчуванням в F # все є константами. Ви можете зробити змінну змінною, але тільки явним чином. За замовчуванням поведінку прямо протилежно тому, до чого ви звикли в імперативних мовах:

let mutable myVariable = 0
myVariable <- 15

Логічне розпізнавання типів і чутливість до прогалин і відступом


F # дозволяє оголошувати змінні і значення без зазначення їх типів, тому можна подумати, ніби F # є динамічним мовою, але це не так. Важливо розуміти, що F # такий же статичний мову, як C # або C + +. Однак F # має потужну систему логічного розпізнавання типів (type inference), яка дозволяє в багатьох місцях не вказувати типи об’єктів. Це спрощує синтаксис і робить його лаконічніше, в той же час зберігаючи безпеку типів, притаманну статичним мов.


Хоча такі системи логічного розпізнавання типів насправді відсутні в імперативних мовах, саме по собі розпізнавання типів не пов’язане безпосередньо з функціональним програмуванням. Однак розпізнавання типів – критична важлива концепція, яку потрібно зрозуміти, якщо ви хочете освоїти F #. На щастя, якщо ви знаєте C #, то напевно вже знайомі з базовою концепцією логічного розпізнавання типів через ключового слова var:

// Here, the type is explictily given
Dictionary<string, string> dictionary =
new Dictionary<string, string>();

// but here, the type is inferred
var dictionary = new Dictionary<string, string>();


Обидві рядки коду на C # створюють нові змінні, які статично типізуються як Dictionary , але в другому випадку ключове слово var повідомляє компілятору логічно визначати тип змінної за вас. F # виводить цю концепцію на новий рівень. Наприклад, ось функція add в F #:

let add x y =
x + y

let four = add 2 2


У наведеному вище коді немає жодної вказівки типу, але F # Interactive розкриває статичну типізацію:

val add : int -> int -> int
val four : int = 4

Сенс стрілок я докладніше поясню потім, а поки ви можете інтерпретувати це так: функція add приймає два int-аргументу, а four є значенням типу int. Компілятор F # зміг логічно розпізнати всі типи, виходячи з визначень add і four. Компілятор використовує при цьому правила, які виходять за рамки цієї статті, але, якщо вас це цікавить, ви можете дізнатися більше в F # Developer Center.


Логічне розпізнавання типів – один із способів, за допомогою яких F # видаляє зайвий “шум” з вашого коду, але зверніть увагу ще й на відсутність фігурних дужок і ключових слів, що позначають тіло функції add або її значення, що повертається. А вся справа в тому, що F # за умовчанням є мовою, чутливим до прогалин і відступами. У F # ви вказуєте тіло функції простими відступами, а повертається значення – Тим, що воно знаходиться в останньому рядку функції. За аналогією з розпізнаванням типів чутливість до прогалин і відступом не має прямого відношення до функціонального програмування, але ви повинні бути знайомі з цією концепцією, щоб користуватися F #.


Побічні ефекти


Тепер ви знаєте, що функціональне програмування відрізняється від імперативного тим, що спирається на незмінні значення, а не на модифікуються змінні, але сам по собі цей факт не дуже корисний. Наступне, в чому нам належить розібратися, – побічні ефекти (side effects).


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


Рис. 1 Побічні ефекти змінювані змінних

public MemoryStream GetStream() {
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.WriteLine(“line one”);
writer.WriteLine(“line two”);
writer.WriteLine(“line three”);
writer.Flush();
stream.Position = 0;
return stream;
}

[TestMethod]
public void CausingASideEffect() {
using (var reader = new StreamReader(GetStream())) {
var line1 = reader.ReadLine();
var line2 = reader.ReadLine();

Assert.AreNotEqual(line1, line2);
}
}


При першому виклику ReadLine потік даних зчитується, поки не зустрінеться символ перекладу на новий рядок. Потім ReadLine повертає весь текст аж до нового рядка. Між цими операціями змінна змінна, представляє позицію в потоці даних, оновлюється. Це і є побічний ефект. При другому виклик ReadLine значення цієї змінної (Position) вже змінилося, тому ReadLine повертає інше значення.


А тепер розглянемо одне з найважливіших наслідків використання побічних ефектів. По-перше, погляньте на простий клас PiggBank і деякі методи для роботи з ним ( рис. 2).


Рис. 2 Змінні PiggyBank

public class PiggyBank{
public PiggyBank(int coins){
Coins = coins;
}

public int Coins { get; set; }
}

private void DepositCoins(PiggyBank piggyBank){
piggyBank.Coins += 10;
}

private void BuyCandy(PiggyBank piggyBank){
if (piggyBank.Coins < 7)
throw new ArgumentException(
“Not enough money for candy!”, “piggyBank”);

piggyBank.Coins -= 7;
}


Якщо у вас є свиня-скарбничка з п’ятьма монетками всередині, ви можете викликати DepositCoins до BuyCandy, але зворотне призведе до генерації виключення:

// this works fine
var piggyBank = new PiggyBank(5);

DepositCoins(piggyBank);
BuyCandy(piggyBank);

// but this raises an ArgumentException
var piggyBank = new PiggyBank(5);

BuyCandy(piggyBank);
DepositCoins(piggyBank);


Опції BuyCandy і DepositCoins оновлюють стан свині-скарбнички через побічний ефект. Відповідно поведінка кожної функції залежить від стану цієї скарбнички. Оскільки число монеток змінюване, порядок виклику цих функцій має значення. Іншими словами, між двома цими методами існує неявна залежність.


Давайте зробимо число монеток тільки для читання, щоб імітувати неизменяемую структуру даних. На рис. 3 показано, що BuyCandy і DepositCoins тепер повертають нові об’єкти PiggyBank замість оновлення існуючого PiggyBank.


Рис.3 Незмінні PiggyBank

public class PiggyBank{
public PiggyBank(int coins){
Coins = coins;
}

public int Coins { get; private set; }
}

private PiggyBank DepositCoins(PiggyBank piggyBank){
return new PiggyBank(piggyBank.Coins + 10);
}

private PiggyBank BuyCandy(PiggyBank piggyBank){
if (piggyBank.Coins < 7)
throw new ArgumentException(
“Not enough money for candy!”, “piggyBank”);

return new PiggyBank(piggyBank.Coins – 7);
}


Як і раніше, якщо ви спробуєте викликати BuyCandy до DepositCoins, ви отримаєте виняток:

// still raises an ArgumentException
var piggyBank = new PiggyBank(5);

BuyCandy(piggyBank);
DepositCoins(piggyBank);


Але навіть якщо ви поміняєте порядок викликів на зворотний, ви отримаєте той же результат:

// now this raises an ArgumentException,  too!
var piggyBank = new PiggyBank(5);

DepositCoins(piggyBank);
BuyCandy(piggyBank);


Тут BuyCandy і DepositCoins залежать тільки від своїх вхідних аргументів, так як число монеток незмінно. Ви можете виконувати ці функції в будь-якому порядку, і результат буде одним і тим же. Неявна залежність між функціями зникла. Однак, якщо ви хочете успішного виконання BuyCandy, то повинні зробити результат BuyCandy залежним від виходу DepositCoins. Вводити таку залежність потрібно явним чином:

var piggyBank = new PiggyBank(5);
BuyCandy(DepositCoins(piggyBank));

Це тонка різниця з далекосяжними наслідками. Загальна змінюване стан і неявні залежності є джерелом деяких з найбільш важковловимий “багів” в імперативному коді і причиною, по якій багатопотокове програмування настільки складно в імперативних мовах. Коли доводиться дбати про порядок виконання функцій, ви повинні спиратися на громіздкі механізми блокування, інакше все піде наперекосяк. Чисто функціональні програми вільні від побічних ефектів і неявних залежностей синхронізації, так що порядок виконання функцій не має ніякого значення. Тобто вам не потрібно хвилюватися про механізми блокування та інших, вкрай схильних до помилок методиках багатопотокового програмування.


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


У F # ви оцінюєте функції з їх значеннями результатів, а не по побічних ефектів. В імперативних мовах функції зазвичай викликаються для виконання чого-небудь, а у функціональних – для отримання якогось результату. Ви можете побачити це в F #, глянувши на вираз if:

let isEven x =
if x % 2 = 0 then
“yes”
else
“no”

Ви вже знаєте, що в F # останній рядок функції це її значення, що повертається, але в цьому прикладі в останньому рядку функції міститься вираз if. Це не трюк компілятора. У F # навіть вирази if спроектовані на повернення значень:

let isEven2 x =
let result =
if x % 2 = 0 then
“yes”
else
“no”
result

Значення result має тип string і привласнюється прямо висловом if. Тут є аналогія з тим, як працює умовний оператор в C #:

string result = x % 2 == 0 ? “yes” : “no”;

Умовний оператор повертає значення, не створюючи побічного ефекту. Це більш функціональний підхід. На противагу цьому вираз if в C # більше імперативне, так як не повертає результат. Все, що воно робить, викликає побічні ефекти.


Композиція функцій


Тепер, коли ви побачили деякі з переваг функцій, вільних від побічних ефектів, ви готові задіяти їх повний потенціал в F #. Для початку розглянемо код на C #, який зводить у квадрат числа від 0 до 10:

IList<int> values = 0.Through(10).ToList();

IList<int> squaredValues = new List<int>();

for (int i = 0; i < values.Count; i++) {
squaredValues.Add(Square(values[i]));
}


Не рахуючи допоміжних методів Through і Square, цей код цілком стандартний для C #. Хороші програмісти на C #, ймовірно, поморщились б від мого використання циклу for замість foreach – і були б праві. Сучасні мови на зразок C # надають цикли foreach як абстракції для спрощення перебору перерахувань, позбавляючи від необхідності явних індексаторів. У цьому вони досягли успіху, але погляньте на код на рис. 4.


Рис. 4 Використання циклів foreach

IList<int> values = 0.Through(10).ToList();

// square a list
IList<int> squaredValues = new List<int>();

foreach (int value in values) {
squaredValues.Add(Square(value));
}

// filter out the even values in a list
IList<int> evens = new List<int>();

foreach(int value in values) {
if (IsEven(value)) {
evens.Add(value);
}
}

// take the square of the even values
IList<int> results = new List<int>();

foreach (int value in values) {
if (IsEven(value)) {
results.Add(Square(value));
}
}


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


Функціональні програмісти використовують інший підхід. Замість створення абстракцій зразок циклів foreach, що допомагають перебирати списки, вони застосовують функції, вільні від побічних ефектів:

let numbers = {0..10}
let squaredValues = Seq.map Square numbers

Цей код на F # теж зводить у квадрат послідовність чисел, але робить це за допомогою функції більш високого порядку. Це просто функції, які приймають в якості вхідного аргументу іншу функцію. В даному випадку функція Seq.map приймає функцію Square в якості аргументу. Вона застосовує цю функцію до кожного числа в послідовності і повертає послідовність чисел, зведених у квадрат. Саме з функцій більш високого порядку багато хто говорить, що у функціональному програмуванні функції використовуються як дані. Це означає лише те, що функції можна передавати як параметри або привласнювати значенням або змінним точно так само, як типи int або string. У термінології C # функції більш високого порядку дуже сильно нагадують концепцію делегатів і лямбда-виразів.


Застосування функцій більш високого порядку – одна з методик, які роблять функціональне програмування таким потужним. З їх допомогою ви можете виділити дубльованих в циклах foreach код і інкапсулювати його в автономні і вільні від побічних ефектів функції. Кожна з цих функцій виконує одну невелику операцію, яку обробляв би код всередині відповідного циклу foreach. Оскільки вони вільні від побічних ефектів, ви можете комбінувати їх для створення більш читаного і простого в супроводі коду, який робить те ж саме, що й цикли foreach:

let squareOfEvens =
numbers
/> Seq.filter IsEven
/> Seq.map Square

Єдина незрозуміла частина цього коду – оператор />. Цей оператор використовується для більшої читабельності коду, дозволяючи вам змінювати порядок аргументи у функції, щоб останній аргумент першою трапилася вам на очі. Його визначення дуже просте:

let (/>) x f = f x

Без оператора /> код squareOfEvens виглядав би так:

let squareOfEvens2 =
Seq.map Square (Seq.filter IsEven numbers)

Якщо ви застосовуєте LINQ, то подібне використання функцій вищого порядку має видатися вам дуже знайомим. А вся справа в тому, що коріння LINQ йдуть глибоко в функціональне програмування. По суті, ви можете легко адаптувати завдання зведення у квадрат парних чисел під C # із застосуванням методів LINQ:

var squareOfEvens =
numbers
.Where(IsEven)
.Select(Square);

Це транслюється в такий синтаксис LINQ-запиту:

var squareOfEvens = from number in numbers
where IsEven(number)
select Square(number);

Використання LINQ в коді на C # або Visual Basic дозволяє експлуатувати деякі можливості функціонального програмування в повсякденній роботі. І це відмінний спосіб освоєння методик такого програмування.


Коли ви почнете регулярно використовувати функції більш високого порядку, ви врешті-решт прийдете до такої ситуації, в якій захочете створити невелику, дуже специфічну функцію для передачі у функцію більш високого порядку. Для вирішення цього завдання “функціональні” програмісти використовують лямбда-функції. Це просто функції, які ви визначаєте, не привласнюючи імен. Зазвичай вони дуже маленькі і мають суто специфічне застосування. Наприклад, ось ще один спосіб, яким ви могли б зводити в квадрат парні числа, використовуючи лямбда:

let withLambdas =
numbers
/> Seq.filter (fun x -> x % 2 = 0)
/> Seq.map (fun x -> x * x)

Єдина відмінність між цим і попереднім кодом полягає в тому, що Square і IsEven визначені як лямбда. У F # ви оголошуєте лямбда-функцію ключовим словом fun. Лямбда слід використовувати тільки для оголошення функцій одного специфічного застосування, тому що їх складно задіяти поза області видимості, в якій вони були визначені. З цієї причини Square і IsEven – поганий вибір на роль лямбда-функцій, оскільки вони корисні в багатьох ситуаціях.


Каррінг і часткове застосування


Тепер ви знаєте майже всі основи, необхідні для того, щоб приступити до роботи з F #, але є ще одна концепція, яку ви повинні освоїти. У попередніх прикладах оператор /> і стрілки в сигнатурах типів з F # Interactive тісно пов’язані з концепцією так званого каррінга.


Каррінг (currying) – це розбиття функції з безліччю аргументів на серію функцій, кожна з яких приймає один аргумент, і в кінцевому рахунку вони дають той же результат, що і початкова функція. Каррінг, мабуть, є в цій статті найважчою концепцією для. NET-розробника, особливо через те, що його часто плутають з частковим застосуванням (partial application). Роботу обох концепцій ілюструє наступний приклад:

let multiply x y =
x * y

let double = multiply 2
let ten = double 5


Прямо зараз ви повинні побачити поведінка, що відрізняється від того, яке ви спостерігали б в більшості імперативних мов. Другий вираз створює нову функцію double передачею одного аргументу у функцію, яка приймає два. Результат – функція, яка приймає один аргумент типу int і повертає те ж саме, як якщо б ви викликали multiply з x, рівним 2, і y, рівним цьому аргументу. По своїй поведінці цей код еквівалентний:

let double2 z = multiply 2 z

Найчастіше в такому разі помилково вважають, що multiply розбивається до double. Але це вірно лише частково. Функція multiply піддається каррінгу, однак це відбувається, коли вона визначена, так як в F # функції піддаються каррінгу за замовчуванням. Коли створюється функція double, точніше сказати, що функція multiply застосовується частково.


Давайте пройдемо всі етапи в деталях. Ще раз повторю, що при каррінге функція з безліччю аргументів розбивається на серію функцій з одним аргументом, які в кінцевому рахунку дають той же результат, що і вихідна. Функція multiply має наступну сигнатуру типу згідно F # Interactive:

val multiply : int -> int -> int

До цього моменту ми розшифрували, що multiply є функцією з двома аргументами типу int і повертається значенням того ж типу. Тепер я поясню, що відбувається насправді. Функція multiply насправді складається з двох функцій. Перша приймає один аргумент типу int і повертає іншу функцію, зрештою зв’язує x з конкретним значенням. Ця функція також приймає один аргумент типу int, яке можна вважати значенням, пов’язують з y. Після виклику цієї другої функції x і y пов’язані зі своїми значеннями, тому результатом є твір x на y, як визначено в тілі функції double.


Щоб створити double, перша функція в ланцюжку функцій multiply оцінюється як частково застосовувана multiply. Отриманою функції присвоюється ім’я double. Коли оцінюється double, вона використовує свій аргумент разом з частково застосованим значенням для створення результату.


Використання F # і функціонального програмування


Тепер, коли у вас є достатній словник для того, щоб почати роботу з F # і зануритися у функціональне програмування, перед вами відкривається безліч варіантів, що робити далі.


Вікно F # Interactive дозволяє досліджувати код на F # і швидко створювати сценарії (scripts) F #. Воно також корисно для рутинної перевірки поведінки бібліотечних функцій. NET без звернення до довідкових файлів або пошуку в Інтернеті.


F # чудовий в вираженні складних алгоритмів, тому ви можете інкапсулювати відповідні частини коду свого застосування в F #-бібліотеки, щоб потім викликати їх з інших. NET-мов. Це особливо корисно в інженерних чи паралельних програмах.


Нарешті, ви можете використовувати методики функціонального програмування в своїй повсякденній розробці для. NET навіть без написання коду на F #. Просто використовуйте LINQ замість циклів for або foreach. Спробуйте застосовувати делегати для створення функцій вищого порядку. Обмежте себе у використанні змінності і побічних ефектів у своїх імперативних мовах. Як тільки ви почнете писати код у функціональному стилі, ви дуже скоро захочете створювати більше коду на F #


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


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

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

Ваш отзыв

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

*

*