Аудитория была бы людьми с
небольшой опыт программирования,
Действительно ли возможно объяснить функциональное программирование, пусть наряду с ОО или процедурным или любой парадигмой программирования людям без большого опыта программирования?
или людей, которые имеют только
объектно-ориентированный опыт.
Ну, наверное, лучшими примерами будет преобразование известных шаблонов проектирования в их функциональный эквивалент. Давайте рассмотрим канонический пример преобразования списка целых в список строк:
using System;
namespace Juliet
{
interface IConvertor<T, U>
{
U Convert(T value);
}
class Program
{
static U[] Convert<T, U>(T[] input, IConvertor<T, U> convertor)
{
U[] res = new U[input.Length];
for (int i = 0; i < input.Length; i++)
{
res[i] = convertor.Convert(input[i]);
}
return res;
}
class SquareInt : IConvertor<int, string>
{
public string Convert(int i)
{
return (i * i).ToString();
}
}
class ScaleInt : IConvertor<int, string>
{
readonly int Scale;
public ScaleInt(int scale)
{
this.Scale = scale;
}
public string Convert(int i)
{
return (i * Scale).ToString();
}
}
static void Main(string[] args)
{
int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
string[] squared = Convert<int, string>(nums, new SquareInt());
string[] tripled = Convert<int, string>(nums, new ScaleInt(3));
}
}
}
Он простой, читаемый, объектно-ориентированный, даже универсальный, так что он работает для любых произвольных типов, так в чем же проблема? Для начала, это раздутый: у меня есть определение интерфейса и две реализации интерфейса. Что если мне понадобится еще одно обращение? Ну, мне нужна другая реализация интерфейса - она может быстро выйти из-под контроля.
Когда вы думаете об этом, класс IConvertor<T, U>
- это просто оболочка вокруг единственной функции с именем Convert
- класс буквально существует, чтобы помочь нам передать Convert
другим функциям. Если вы можете это понять, то вы уже понимаете основные принципы, лежащие в основе функций, как первоклассные значения - функциональное программирование - это передача функций другим функциям почти так же, как вы передаете человека, int или строку.
Люди обычно предпочитают функциональное программирование, потому что оно помогает им избегать однотипных интерфейсов и реализаций. Вместо того, чтобы передавать класс, мы просто передаем функцию по имени или анонимно:
using System;
namespace Juliet
{
class Program
{
static U[] Convert<T, U>(T[] input, Func<T, U> convertor)
{
U[] res = new U[input.Length];
for (int i = 0; i < input.Length; i++)
{
res[i] = convertor(input[i]);
}
return res;
}
static string SquareInt(int i)
{
return (i * i).ToString();
}
static void Main(string[] args)
{
int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
string[] squared = Convert<int, string>(nums, SquareInt); // pass function by name
string[] tripled = Convert<int, string>(nums, i => (i * 3).ToString()); // or pass anonymously
}
}
}
Хорошо, теперь у нас точно такая же программа с гораздо меньшим количеством строк кода, она удобочитаема и очень наглядно показывает, что она делает.
Теперь кто-то может сказать: «Это изящный трюк, но когда бы я его использовал» - есть много случаев, когда вы хотели бы передавать функции таким образом. Это дает вам гораздо больше возможностей абстрагировать поток управления вашими программами новыми способами. Адаптировано из примера, показанного здесь , рассмотрим класс, который обрабатывает файлы:
class FileFunctions
{
internal void SaveFile()
{
SaveFileDialog fileDialog = new SaveFileDialog();
fileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
if (fileDialog.ShowDialog() == DialogResult.OK)
{
File.AppendAllText(fileDialog.FileName, MyDocument.Data);
}
}
internal void WriteFile()
{
OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
if (fileDialog.ShowDialog() == DialogResult.OK)
{
MyDocument.Data = File.ReadAllText(fileDialog.FileName);
}
}
}
Тьфу. Код повторяется, но он выполняет операции над разными классами и вызывает разные методы. Как бы вы абстрагировали дублированный код во вселенной OO? В функциональном программировании это тривиально:
class FileFunctions
{
internal void ShowDialogThen(FileDialog dialog, Action<FileDialog> f)
{
dialog.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
if (dialog.ShowDialog() == DialogResult.OK)
{
f(dialog);
}
}
internal void SaveFile()
{
ShowDialogThen(
new SaveFileDialog(),
dialog => File.AppendAllText(dialog.FileName, MyDocument.Data));
}
internal void WriteFile()
{
ShowDialogThen(
new OpenFileDialog(),
dialog => { MyDocument.Data = File.ReadAllText(dialog.FileName); });
}
}
Высокий.