Джоэл Спольски о функциональном программировании
От автора сайта. Ниже представлена краткая выжимка статьи Джоэла Спольски «А ваш язык программирования так может?».
Краткая, но достаточная, чтобы понять главные идеи. Статья написана в 2006 году и какие-то сведения уже устарели.
Например, Спольски тогда не мог предвидеть, что C++ и другие языки будет активно пополняться функциональными возможностями.
Однако, это не умаляет важности мыслей и идей Спольски. Хотя не всё так гладко и радужно, как он рисует, но к этому ещё вернёмся в послесловии.
Си позволяет объявить тип «функция». Можно заранее объявить некую функцию, а затем передать её в другую функцию, один (или несколько) из параметров которой имеет тип «функция». В эту другую функцию передаётся указатель на объявленную ранее функцию. Безымянные же функции можно не объявлять заранее, а объявить прямо по месту употребления.
f1 (param1, param2, f2(x) {return x*x;}); // Javascript
Проделывание чего-нибудь с каждым элементом массива достаточно распространено, и вы можете написать функцию, которая делает это для вас:
function map(fn, a)
{
for (i = 0; i < a.length; i++)
{
a[i] = fn(a[i]);
}
}
Теперь вы можете переписать предыдущий код:
map( function(x){return x*2;}, a );
map( alert, a );
Другая распространенная операция с массивами — как-нибудь объединить все его значения.
function sum(a)
{
var s = 0;
for (i = 0; i < a.length; i++)
s += a[i];
return s;
}
function join(a)
{
var s = "";
for (i = 0; i < a.length; i++)
s += a[i];
return s;
}
alert(sum([1,2,3]));
alert(join(["a","b","c"]));
sum и join выглядят так одинаково, что вы можете захотеть абстрагировать их сущность в общую функцию, которая объединяет элементы массива в одно значение:
function reduce(fn, a, init)
{
var s = init;
for (i = 0; i < a.length; i++)
s = fn( s, a[i] );
return s;
}
function sum(a)
{
return reduce( function(a, b){ return a + b; },
a, 0 );
}
function join(a)
{
return reduce( function(a, b){ return a + b; },
a, "" );
}
Во многих старых языках программирования просто нет способа проделать этот трюк. Другие языки позволят вам сделать это, но достаточно сложно
(например, в Си есть указатели на функции, но вы должны объявить и определить функцию где-либо в другом месте).
Объектно-ориентированные языки программирования не вполне уверены, что вам следует разрешить делать что-нибудь с функциями.
Java требует от вас создать отдельный объект с одним методом, так называемый функтор, если вы хотите представить функцию как объект первого класса
(first class object, т. е. сущности языка программирования, которые можно использовать без ограничений при хранении в переменных,
хранении в более сложных структурах, для передачи в функции в качестве аргумента, и возвращения в качестве результата).
Добавьте к этому факт, что множество ОО-языков вынуждают вас создавать отдельный файл для каждого класса, и это делает код неуклюжим очень быстро.
Если ваш язык программирования требует от вас использования функторов, вы не получите всех преимуществ современной среды программирования.
Посмотрите, сможете ли вы получить ваши деньги обратно.
Какое преимущество вы реально получите от написания крошечных функций, которые не делают ничего более, чем пробегают по массиву и проделывают что-нибудь с каждым элементом?
Какое преимущество вы реально получите от написания крошечных функций, которые не делают ничего более, чем пробегают по массиву и проделывают что-нибудь с каждым элементом?
Хорошо, давайте вернемся к функции map . Когда вам нужно сделать что-нибудь с каждым элементом массива по очереди на самом деле вам, возможно, не важно в какой очередности вы это делаете.
Вы можете пробежаться по массиву вперед или назад и получить одинаковый результат, верно?
В действительности, если у вас в распоряжении есть два процессора, может быть вы напишете какой-нибудь код для того,
чтобы каждый процессор обрабатывал половину элементов, и внезапно map становится в два раза быстрее.
Или, возможно, чисто гипотетически, у вас есть сотни тысяч серверов в нескольких центрах обработки данных по всему миру,
и у вас есть действительно большой массив, содержащий, скажем, опять чисто гипотетически, всё содержимое Интернета.
Теперь вы можете запустить map на тысячах компьютеров, каждый из которых будет штурмовать маленькую часть проблемы.
Таким образом сейчас, например, написание какого-либо по-настоящему быстрого кода для поиска во всём содержимом Интернета
просто сводится к вызову функции map с простой функцией поиска строки в качестве аргумента.
Действительно интересная вещь, на которую я хочу обратить ваше внимание здесь, что как только вы подумаете о map и reduce
как о функциях, которые все могут использовать (и используют их), вам нужен только супергений, чтобы написать тяжелый код,
чтобы запустить map и reduce на глобальном параллельном массиве компьютеров, и весь старый код,
который отлично работал, когда вы запускали небольшие циклы, по-прежнему работает, только в огромное число раз быстрее,
что значит он может использоваться для решения действительно больших проблем за секунду.
Позвольте мне повторить. Абстрагируя концепцию циклов, вы можете осуществить обработку циклов как вам захочется, включая способ, который отлично масштабируется с появлением дополнительного «железа».
Теперь вы поймете кое-что из того, что я когда-то писал, выражая недовольство по поводу студентов, которые никогда не учили ничего кроме Java.
Без понимания функционального программирования вы не сможете придумать MapReduce — алгоритма, который делает Google столь хорошо масштабируемым.
Термины Map и Reduce пришли из Lisp и функционального программирования.
MapReduce понятен любому, кто помнит со скамьи вуза, что истинно функциональные программы не имеют побочных эффектов и поэтому легко распараллеливаемы.
Очень показателен тот факт, что в Google изобрели MapReduce, а в Microsoft нет, и это говорит кое-что о том, почему Microsoft до сих пор играет в догонялки,
пытаясь заставить работать основные функции поисковой машины, в то время как в Google перешли к следующей проблеме: построению Skynet величайшего в мире параллельного суперкомпьютера.
Я не думаю, что Microsoft действительно понимает, насколько они отстали на этом пути.
OK. Надеюсь, я вас уже убедил, что языки программирования, где функции — объекты первого класса, предоставляют вам больше возможностей для абстрагирования.
Это значит, что ваш код меньше, надежнее, лучше для повторного использования и более масштабируемый.
Множество приложений Google используют MapReduce и они все выигрывают, когда кто-нибудь оптимизирует его или исправляет ошибку.
А сейчас я буду немного непоследовательным и буду утверждать, что наиболее эффективны те среды программированию, которые позволяют работать на разных уровнях абстракции.
Старый недобрый FORTRAN даже не позволял создавать функции.
Си содержит указатели на функции, но они уродливы, не могут быть безымянными и их реализация должна быть написана где-то в месте отличном от того, где они используются.
Java заставляет использовать функторы, которые ещё уродливее. Как заметил Steve Yegge, Java — это Королевство Существительных.
Послесловие автора сайта. Идея понятна, но, как всегда, дьявол прячется в деталях.
Есть возражения и сомнения как по получению доступа миллиона процессоров к миллиону элементов массива,
так и по получению единственного результата из миллиона процессоров.
Опубликовано: 2022.01.02, последняя правка: 2023.03.05 01:06
Добавить свой отзыв
Написать автору можно на электронную почту mail(аt)compiler.su
|