#functional-programming
#функциональное программирование
Вопрос:
Исходя из ООП, в последние недели я начал читать о функциональном программировании и начал изучать Haskell. Очень часто я читал, что (чистые) функции легче тестировать, поскольку в них нет сложных процедур настройки и демонтажа. Это звучит разумно, и я соглашался. Составление этих функций позволяет мне создавать более сложные функции с очень хорошо протестированной «базой».
Когда я начал писать свои первые программы, некоторые функции получали все больше и больше параметров, и поэтому я начал создавать новые типы данных, которые для меня похожи на структуры. Конечно, типы данных объединяют то, что логически принадлежит друг другу, и иногда состоят из других типов данных.
Вызывая функции, затем как
MyTpye = foo(MyTpye, someParam)
немного похоже на выполнение ООП с действительно уродливым синтаксисом, как во времена C, с указателями на функции в структурах:
MyType = foo(this, someParam) = {
...
return modified_copy_of_this;
}
Тестирование этих функций также требует от меня сначала настройки моего нового типа данных, что может быть сложным, и я просто чувствую, что я не сильно выигрываю.
Я полагаю, что я все еще слишком узколоб и сосредоточен на ООП-способе написания кода. У меня просто такое ощущение, что вызов функций для типов данных — это не что иное, как вызов методов для неизменяемых объектов (конечно, каждый установщик должен был бы возвращать измененную версию объекта).
Может кто-нибудь, пожалуйста, прояснить это немного для меня?
Большое вам спасибо!
Я добавил этот пример на Haskell. Это мои первые шаги, и я попытался избежать публикации уродливого кода, подобного этому. Это только первые части алгоритма обучения переформулированию. Настройка «среды» с доступными состояниями, действиями и функцией вознаграждения.
type Action = [Double]
type State = [Double]
data StateSet = StateSet [State] deriving (Show)
data ActionSet = ActionSet [Action] deriving (Show)
data Environment = Environment {
availableStates :: StateSet,
availableActions:: ActionSet,
currentState :: State,
rewardfnc :: State -> Action -> Double,
lastReward :: Double
}
rewardFunction :: State -> Action -> Double
rewardFunction s a = (-1)
doAction :: Environment -> Action -> Environment
doAction env a = Environment (availableStates env) (availableActions env) a (rewardfnc env) ((rewardfnc env) (currentState env) a)
getReward :: Environment -> Double
getReward env = (lastReward env)
states = StateSet [[i,j] | i <- [1..10], j <- [1..10]]
actions = ActionSet [[i,j] | i <- [1..10], j <- [1..10]]
initEnv = Environment states actions [0,0] rewardFunction 0.0
env = doAction initEnv [2,2]
reward = getReward(env)
Комментарии:
1. Не могли бы вы, пожалуйста, привести более конкретный пример (предпочтительно на Haskell, поскольку это то, что вы пытаетесь изучить)? Я не вижу ничего плохого в этом примере (если я читаю его как псевдокод). Возможно, вы можете предоставить определение ваших типов и тестовый пример.
2. @Andre Спасибо, что я добавил пример
3. Просто подсказка для вашего кода: вместо определения типов данных
StateSet
иActionSet
вы могли бы использовать тип данных Set в Data.Set: hackage.haskell.org/package/containers-0.5.6.3/docs/… Затем используйтеSet State
иSet Action
соответственно, и тогда вы сможете использовать целую кучу эффективных функций для выполнения операций с наборами без каких-либо дополнительных усилий
Ответ №1:
Я чувствовал то же самое, что и вы, когда я впервые начал изучать функциональное программирование — это calling functions on data types is nothing else than calling methods on immutable objects
.
Я думаю, важно понимать, что это один из способов взглянуть на FP, а не единственный. Аналогично, методология ООП группировки данных и процедур вместе в классы — это один из способов думать о программировании, но не единственный. Для некоторых проблем ООП приводит к отличным решениям, в то время как для других это не так хорошо.
Приведенный вами пример или выбранный вами способ его решения могут просто оказаться более подходящими для ООП, чем для FP. Не то чтобы вы не могли решить это с помощью FP, просто это кажется немного неуклюжим и вынужденным — как вы заметили. Я предполагаю, что причина, по которой этот пример доставляет вам проблемы, заключается в том, что он связан с изменением среды — очень естественным для ООП.
Существуют и другие проблемы, для которых решение FP будет казаться естественным и простым, а версия ООП неудобной и вынужденной. Подумайте о проблемах, решение которых включает преобразование входных данных в выходные:
- входные данные: набор карт (т.Е. таблица базы данных) выходные данные: отчет, сгруппированный по одному из столбцов, отфильтрованный по предикату, с применением к столбцам агрегатных функций
- ввод: текст программы, вывод: дерево синтаксического анализа
- входные данные: дерево синтаксического анализа Выходные данные: статистика по длине функции, именам переменных, количеству комментариев и т.д.
Подводя итог, если вы продолжите функциональное программирование, вы обнаружите, что существуют другие проблемы, для которых хорошо подходит FP, и решения которых намного проще и естественнее, чем они были бы на языках OO.
Комментарии:
1. Спасибо за быстрый ответ. Да, я полагаю, мне нужно изменить свой взгляд на проблему.