Сейчас модно говорить о ФП – что это такое, зачем и как его используют. Мне же хотелось бы рассматривать ФП как некоторый способ более эффективно реализовывать поставленную задачу. Т.е. не как просто новомодный (хотя ФП уже старо как мир) финт ушами, а как реальное практическое средство.
Один из элементов ФП, позволяющих эффективно его использовать являются “ленивые вычисления”. Принцип такой – зачем вычислять то, что возможно нам не понадобится.
Для того, чтоб лучше понять чем же это может быть удобно, я для себя составил вот такой иллюстрирующий пример:
Допустим у нас есть задача: написать функцию, принимающую три числовых параметра (a, b, c), и возвращающую сумму a+b если a>0 и a+c если a<0.
Простейшая реализация (C#):
int function(int a, int b, int c)
{
return a+a>0?b:c;
}
Но при вызове этой функции нам понадобится передать все три параметра. Но ведь реально нужны только 2 (в зависимости от значения первого или второго параметра). А если вычисление значений каждого из параметров является трудоёмка задача (выполнение запросов к базе данных, вычитка из большого документа или просто тяжело-вычисляемое значение), то нет смысла его делать.
Вот тут то и приходит на помощь идея ФП. А точнее то, что основным объектов является функция. Можно просто передать как параметр не значение, а функцию, которая возвращает это значение.
Таким образом, если мы перепишем эту функцию так, чтоб она оперировала не с цифрами, а с функциями, то получим, что при получении результата, будут вычисляться только те аргументы, которые реально нужны.
Теперь небольшой пример из совсем почти не функционального языка – C# 2.0:
Для начала перепишем функцию так, чтоб она оперировала с функциями (“делегатами”):
private delegate int IntFunction();
private static IntFunction funct(IntFunction a, IntFunction b, IntFunction c)
{
return delegate
{
int aValue = a(); return aValue > 0 ? aValue + b() : aValue + c();
};
}
Далее, допустим есть функция, которая очень долго работает. Для примера, пусть она возвращает число, переданное в качестве параметра:
private static IntFunction CreateInt(int a)
{
return delegate
{
Console.WriteLine("Computing for long..long time, resulting "+a);
return a;
};
}
Ну а теперь сам вызов:
Console.WriteLine("Result is {0}",funct(CreateInt(1), CreateInt(2), CreateInt(3))());
В результате получим:
Computing for long..long time, resulting 1
Computing for long..long time, resulting 2
Result is 3
Как видно, функция, возвращающая “3” даже не была запущенна.
Ну и само-собой, можно передавать результат выполнения функции как параметр для самой же функции, что позволяет выстраивать конструкции по типу:
Console.WriteLine("Result is {0}", funct(funct(CreateInt(1), CreateInt(2), CreateInt(3)), CreateInt(4), CreateInt(5))());
В результате которых будет реально толь3о 3 раза вызвана наша “долгая” функция, вместо 5, как было бы при обычном подходе.
Вот такой простой и имхо понятный пример того, как можно понять смысл использования “ленивых вычислений”, и использовать в “не функциональных” языках.
1 comment:
Post a Comment