Если моя функция удовлетворяет ниже двум требованиям, я полагаю, что функция, Sum
возвращающая суммирование элементов в списке, где элемент оценивается как истинное для данного условия, может считаться чистой функцией, не так ли?
1) Для данного набора i / p, то же o / p возвращается независимо от времени вызова функции
2) не имеет побочных эффектов
public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
int result = 0;
foreach(var item in numbers)
if(predicate(item)) result += item;
return result;
}
Пример : Sum(x=>x%2==0, new List<int> {1,2,3,4,5...100});
Причина, по которой я задаю этот вопрос, заключается в том, что я вижу почти все, где люди советуют избегать операторов присваивания и циклов, потому что это императивный стиль программирования. Так что же может пойти не так с приведенным выше примером, который использует циклы и оператор присваивания в контексте программирования функций?
c#
functional-programming
imperative-programming
pure-function
rahulaga_dev
источник
источник
item
переменная мутирует в цикле.Ответы:
Что в функциональном программировании имеет значение?
Функциональное программирование в принципе декларативно . Вы говорите, каков ваш результат, а не как его вычислить.
Давайте посмотрим на действительно функциональную реализацию вашего фрагмента. В Хаскеле это было бы:
Понятно, каков результат? Именно такова сумма чисел, соответствующих предикату. Как это вычисляется? Мне все равно, спросите компилятор.
Можно сказать, что используя
sum
иfilter
это трюк, и это не считается. Позвольте реализовать это без этих помощников (хотя лучшим способом было бы реализовать их в первую очередь).Решение «Функциональное программирование 101», которое не использует
sum
, с рекурсией:Все еще довольно ясно, каков результат с точки зрения одного вызова функции. Это либо
0
, либоrecursive call + h or 0
, в зависимости отpred h
. Все еще довольно просто, даже если конечный результат не сразу очевиден (хотя с небольшой практикой это действительно выглядит какfor
цикл).Сравните это с вашей версией:
Что в итоге? О, я вижу: одно
return
заявление, никаких сюрпризов здесьreturn result
.А что есть
result
?int result = 0
? Не кажется правильным. Вы делаете что-то позже с этим0
. Хорошо, вы добавляетеitem
s к нему. И так далее.Конечно, для большинства программистов это довольно очевидно, что происходит в такой простой функции, как эта, но добавьте несколько дополнительных
return
утверждений или около того, и это вдруг станет сложнее отследить. Весь код посвящен тому , как и что читателю остается понять - это явно очень императивный стиль. .Итак, переменные и циклы не так?
Нет.
Есть много вещей, которые они гораздо легче объяснить, и много алгоритмов, которые требуют изменяемого состояния, чтобы быть быстрым. Но переменные по своей сути обязательны, объясняя, как вместо того, что , и давая небольшой прогноз того, что их значение может быть несколько строк спустя или после нескольких итераций цикла. Циклы обычно требуют состояния, чтобы иметь смысл, и поэтому они также являются обязательными.
Переменные и циклы просто не являются функциональным программированием.
Резюме
Современное функциональное программирование - это больше стиль и полезный способ мышления, чем парадигма. Сильное предпочтение чистых функций заключается в этом мышлении, но на самом деле это лишь малая часть.
Большинство распространенных языков позволяют использовать некоторые функциональные конструкции. Например, в Python вы можете выбрать между:
или
или
Эти функциональные выражения прекрасно подходят для подобных задач и просто делают код короче (а короче - хорошо ). Не стоит бездумно заменять им императивный код, но когда они подходят, они почти всегда являются лучшим выбором.
источник
Использование изменяемого состояния обычно не рекомендуется в функциональном программировании. Как следствие, петли не рекомендуется использовать, потому что петли полезны только в сочетании с изменяемым состоянием.
Функция в целом чистая, и это здорово, но парадигма функционального программирования применима не только на уровне целых функций. Вы также хотите избежать изменяемого состояния также на локальном уровне, внутри функций. И причина в основном та же: избегание изменяемого состояния облегчает понимание кода и предотвращает определенные ошибки.
В вашем случае вы могли бы написать,
numbers.Where(predicate).Sum()
что явно намного проще. И проще означает меньше ошибок.источник
Where
вnumbers.Where(predicate).Sum()
- он используетforeach
цикл.Хотя вы правы в том, что с точки зрения внешнего наблюдателя ваша
Sum
функция чиста, внутренняя реализация явно не чиста - у вас есть сохраненное состояние, вresult
котором вы постоянно мутируете. Одна из причин, по которой следует избегать изменчивого состояния, заключается в том, что он создает большую когнитивную нагрузку на программиста, что, в свою очередь, приводит к большему количеству ошибок [необходима цитата] .Хотя в таком простом примере, как этот, количество изменяемого состояния, которое может быть сохранено, вероятно, достаточно мало, чтобы не вызывать серьезных проблем, общий принцип по-прежнему применяется. Пример из игрушки , как ,
Sum
вероятно, не самый лучший способом , чтобы проиллюстрировать преимущества функционального программирования императивного - попытаться сделать что - то с большим изменяемого государства и преимущества могут стать более ясными.источник