Запутанная часть - петля. Давайте начнем с этого. Цикл обычно преобразуется в функциональный стиль, выражая итерацию одной функцией. Итерация - это преобразование переменной цикла.
Вот функциональная реализация общего цикла:
loop : v -> (v -> v) -> (v -> Bool) -> v
loop init iter cond_to_cont =
if cond_to_cont init
then loop (iter init) iter cond
else init
Он принимает (начальное значение переменной цикла, функция, которая выражает одну итерацию [в переменной цикла]) (условие для продолжения цикла).
Ваш пример использует цикл в массиве, который также разрывается. Эта возможность на вашем императивном языке встроена в сам язык. В функциональном программировании такая возможность обычно реализуется на уровне библиотеки. Вот возможная реализация
module Array (foldlc) where
foldlc : v -> (v -> e -> v) -> (v -> Bool) -> Array e -> v
foldlc init iter cond_to_cont arr =
loop
(init, 0)
(λ (val, next_pos) -> (iter val (at next_pos arr), next_pos + 1))
(λ (val, next_pos) -> and (cond_to_cont val) (next_pos < size arr))
В этом :
Я использую пару ((val, next_pos)), которая содержит видимую снаружи переменную цикла и позицию в массиве, которую скрывает эта функция.
Функция итерации немного сложнее, чем в общем цикле, эта версия позволяет использовать текущий элемент массива. [Это в карри .]
Такие функции обычно называют «сложить».
Я вставил «l» в названии, чтобы указать, что накопление элементов массива осуществляется левоассоциативным образом; имитировать привычку императивных языков программирования перебирать массив от низкого до высокого индекса.
Я вставил «c» в названии, чтобы указать, что эта версия сгиба принимает условие, которое контролирует, когда и когда цикл должен быть остановлен досрочно.
Конечно, такие вспомогательные функции, вероятно, будут легко доступны в базовой библиотеке, поставляемой с используемым функциональным языком программирования. Я написал их здесь для демонстрации.
Теперь, когда у нас есть все инструменты, которые есть в языке в императивном случае, мы можем обратиться к реализации конкретной функциональности вашего примера.
Переменная в вашем цикле - это пара ('answer', логическое значение, которое кодирует, продолжать ли).
iter : (Int, Bool) -> Int -> (Int, Bool)
iter (answer, cont) collection_element =
let new_answer = answer + collection_element
in case new_answer of
10 -> (new_answer, false)
150 -> (new_answer + 100, true)
_ -> (new_answer, true)
Обратите внимание, что я использовал новую переменную new_answer. Это связано с тем, что в функциональном программировании я не могу изменить значение уже инициализированной «переменной». Я не беспокоюсь о производительности, компилятор может повторно использовать память 'answer' для 'new_answer' с помощью анализа времени жизни, если он считает, что это более эффективно.
Включение этого в нашу функцию цикла, разработанную ранее:
doSomeCalc :: Array Int -> Int
doSomeCalc arr = fst (Array.foldlc (0, true) iter snd arr)
«Массив» - это имя модуля, в котором экспортируется функция foldlc.
«кулак», «второй» означают функции, которые возвращают первый, второй компонент своего параметра пары
fst : (x, y) -> x
snd : (x, y) -> y
В этом случае стиль «без точек» повышает удобочитаемость реализации doSomeCalc:
doSomeCalc = Array.foldlc (0, true) iter snd >>> fst
(>>>) - это композиция функций: (>>>) : (a -> b) -> (b -> c) -> (a -> c)
Это то же самое, что и выше, только параметр "arr" не учитывается с обеих сторон определяющего уравнения.
И последнее: проверка регистра (массив == ноль). В лучше разработанных языках программирования, но даже в плохо разработанных языках с некоторой базовой дисциплиной, один использует дополнительный тип для выражения несуществования. Это не имеет ничего общего с функциональным программированием, о котором в конечном итоге и идет речь, поэтому я не занимаюсь этим.
break
иreturn answer
может быть замененаreturn
внутри цикла. В FP вы можете реализовать это раннее возвращение, используя продолжения, см., Например, en.wikipedia.org/wiki/Continuation