Почему плохие циклы for? Или в чем
контекст для циклов, чтобы избежать и
почему?
Если у вашего коллеги есть функциональное программирование, то он, вероятно, уже знаком с основными причинами избегания циклов:
Fold / Map / Filter охватывает большинство случаев использования обхода списка и хорошо подходит для составления функций. Циклы for не являются хорошим шаблоном, потому что они не поддаются компоновке.
В большинстве случаев вы просматриваете список, чтобы сложить (агрегировать), отобразить или отфильтровать значения в списке. Эти функции более высокого порядка уже существуют в каждом основном функциональном языке, поэтому вы редко видите идиому цикла for, используемую в функциональном коде.
Функции высшего порядка - это набор функций, то есть вы можете легко объединить простую функцию в нечто более сложное.
Чтобы привести нетривиальный пример, рассмотрим следующее на императивном языке:
let x = someList;
y = []
for x' in x
y.Add(f x')
z = []
for y' in y
z.Add(g y')
На функциональном языке мы написали бы map g (map f x)
, или мы можем исключить промежуточный список, используя map (f . g) x
. Теперь мы можем, в принципе, исключить промежуточный список из обязательной версии, и это помогло бы немного, но не сильно.
Основная проблема с императивной версией заключается в том, что циклы for являются подробностями реализации . Если вы хотите изменить функцию, вы измените ее реализацию - и в итоге вы изменили много кода.
В качестве примера, как бы вы написали map g (filter f x)
в обязательном порядке? Ну, поскольку вы не можете повторно использовать свой исходный код, который отображает и отображает, вам нужно написать новую функцию, которая фильтрует и отображает вместо этого. И если у вас есть 50 способов отображения и 50 способов фильтрации, то, как вам нужно 50 ^ 50 функций, или вам нужно смоделировать возможность передавать функции в качестве первоклассных параметров, используя шаблон команды (если вы когда-либо пробовали функциональное программирование в Java вы понимаете, какой это может быть кошмар).
Вернувшись в функциональную вселенную, вы можете обобщить map g (map f x)
таким образом, чтобы вы могли заменять map
на filter
или fold
при необходимости:
let apply2 a g b f x = a g (b f x)
И назовите его, используя apply2 map g filter f
или apply2 map g map f
или apply2 filter g filter f
, или как вам нужно. Теперь вы, вероятно, никогда не будете писать подобный код в реальном мире, вы, вероятно, упростите его, используя:
let mapmap g f = apply2 map g map f
let mapfilter g f = apply2 map g filter f
Функции высшего порядка и состав функций дают вам уровень абстракции, который вы не можете получить с помощью императивного кода.
Абстрагирование деталей реализации циклов позволяет плавно менять один цикл на другой.
Помните, for-циклы - это детали реализации. Если вам нужно изменить реализацию, вам нужно изменить каждый цикл for.
Карта / сворачивание / фильтрация абстрагирования от петли. Поэтому, если вы хотите изменить реализацию ваших циклов, вы измените их в этих функциях.
Теперь вы можете задаться вопросом, почему вы хотите абстрагироваться от цикла. Рассмотрим задачу отображения элементов из одного типа в другой: обычно , элементы отображаются по одному, последовательно и независимо от всех других элементов. В большинстве случаев подобные карты являются основными кандидатами для распараллеливания.
К сожалению, детали реализации для последовательных карт и параллельных карт не являются взаимозаменяемыми. Если у вас есть тонна последовательных карт по всему коду, и вы хотите заменить их на параллельные карты, у вас есть два варианта: копировать / вставлять один и тот же код параллельного отображения по всей вашей кодовой базе или абстрагировать логику удаленного отображения в две функции map
и pmap
. Пройдя второй путь, вы уже по колено на территории функционального программирования.
Если вы понимаете назначение композиции функций и абстрагирование деталей реализации (даже таких деталей, как зацикливание), вы можете начать понимать, как и почему функциональное программирование настолько мощно, во-первых.