Дополнительно
Каковы функциональные эквиваленты императивных операторы Break и другие проверки петли?
Позвольте'ы сказать, Я'вэ указанных ниже логики. Как пишут, что в функциональном программировании?
public int doSomeCalc(int[] array)
{
int answer = 0;
if(array!=null)
{
for(int e: array)
{
answer += e;
if(answer == 10) break;
if(answer == 150) answer += 100;
}
}
return answer;
}
Примеры в большинстве блогов, статей... я вижу просто объясняет просто одна прямая функция вперед математику сказать 'Сум'. Но, у меня же логике, что и выше написаны на языке Java и хотел бы перенести, что код на Clojure. Если мы можем'т сделать выше в ФП, то вроде акции для ФП не'т прямо указать это.
Я знаю, что приведенный выше код является абсолютно обязательным условием. Он не был написан с продуманности миграция на ФП в будущем.
36
7
Ближайшим эквивалентом к обходишь массив в большинстве функциональных языков является функцией складывания, т. е. функция, которая вызывает определенные пользователем функции для каждого значения массива, передавая накапливаемое значение по цепочке. Во многих функциональных языках, "фолд" дополняется множеством дополнительных функций, которые предоставляют дополнительные функции, включая возможность остановить на ранней стадии, когда некоторое условие возникает. В ленивых языках (например, в Haskell), ранняя остановка может быть достигнуто, просто не оценив дальше по списку, что вызовет дополнительные значения не будут генерироваться. Поэтому, переводя ваш пример на Хаскеле, я бы написал так:
Разорвать этот вниз на одну строку в случае, если вы'повторно не знакомы с Haskell'ы синтаксис, это работает так:
Определяет тип функции, принимает список целых чисел и возвращает один инт.
На корпусе функция: данный параметр
значения
, возвращаетfoldr1
вызываться с аргументамиобъединить
(мы'МР определение ниже) иценности
.foldr1
есть вариант сложить примитивны, что начинается с аккумулятором установлен на первое значение списка (отсюда и1
в имени функции), а затем объединить их, используя указанный пользователем функции слева направо (что обычно называют права раза, отсюда иР
в имени функции). Так foldr1 Ф [1,2,3]эквивалентно
Ф 1 (Ф 2 3)(или
F(1,ф(2,3)) в обычный C-подобный синтаксис).Определение
объединить
местная функция: она принимает два аргумента,В1
и В2. Когда
В110, то он просто возвращает
В1`. В данном случае, *V2-это не оценивали, так что цикл останавливается здесь.Кроме того, когда v1-это 150, добавляет дополнительные 100 до него, и добавляет В2.
И, если ни одно из этих условий, просто добавляет v1 до V2.
Теперь, это решение является то, что в Хаскеле, потому что тот факт, что право сложить прекращается, если совмещать функции не'т оценить своего второго аргумента вызывается Хаскелл'ленивого вычисления стратегии. Я не'т знаю, что в Clojure, но я верю, что он использует строгие оценки, так что я бы ожидать, чтобы иметь
функции складывать в стандартную библиотеку, которая включает специальную поддержку досрочного расторжения. Это часто называют
foldWhile,
foldUntil` или подобный.Быстрый взгляд в Clojure библиотека документация предполагает, что это немного отличается от большинства функциональных языков в именовании, и что " раз " это'т то, что вы'вновь ищет (он's более продвинутый механизм, направленный на обеспечение параллельных вычислений), но
уменьшить
является более прямого эквивалента. Досрочное расторжение происходит, если снижается функция называется в вашей сочетающий в себе функции. Я'м не уверен на 100% я понимаю синтаксис, но я подозреваю, что вы'вновь ищу что-то вроде этого:Примечание: оба перевода, на Haskell и Clojure, не совсем верно для этого конкретного кода, но они передают общую суть -- см. обсуждение в комментариях ниже, для конкретных проблем с этими примерами.
Вы можете легко преобразовать его в рекурсию. И это хороший хвост-оптимизация рекурсивного вызова.
Псевдокод :
Мне очень нравится Джулс' ответ, но я хотел бы дополнительно отметить, что люди часто не хватает ленивого функционального программирования, который заключается в том, что все не'т должны быть "внутри цикла.&я, например:
Вы можете увидеть, что каждая часть твоей логике может вычисляться в отдельной функции, то пишем вместе. Это позволяет для небольших функций, которые, как правило, гораздо легче устранить. Для вашей игрушки, например, возможно, это добавляет больше сложности, чем он снимает, но в реальном мире код разделить функции зачастую гораздо проще, чем целый.
Большинство список примеры обработки вы увидите использовать такие функции, как
карта
,фильтр
,сумма
и т. д. которые работают на список в целом. Но в вашем случае у вас условно-досрочного выезда - довольно необычным рисунком, который не поддерживает обычные операции список. Так что вам нужно бросить вниз уровень абстракции и использовать рекурсию - что тоже ближе к как императив пример выглядит.Это достаточно прямой (наверное, не идиоматические) перевод в Clojure:
Редактировать: Жюль отметить, что уменьшать в Clojure ду поддержка раннего выхода. Используя это более элегантно:
В любом случае, вы можете сделать что-нибудь в функциональных языках, как вы можете в императивных языках, но зачастую вам придется несколько изменить свое мышление, чтобы найти элегантное решение. В императивном кодирования вы считаете обработки список шаг за шагом, в то время как в функциональных языках вы ищете операция применяется ко всем элементам в списке.
Как отмечали другие ответы, в Clojure есть
снижается
для остановки ранних сокращений:Это лучшее решение для вашей конкретной ситуации. Вы также можете получить много пробег из сочетания
уменьшается
спреобразовывать
, которая позволяет использовать датчики от "карта", "фильтр" и т. д. Однако это далеко не полный ответ на ваш общий вопрос.Побег продолжения обобщенных перерыв и вернуться заявления. Они непосредственно реализуются в некоторых схемах (звонить-с-побег-продолжение
), общий Лисп (
блок+
возвращение,
поймать+
бросать) и даже c (
команду setjmp+
longjmp`). Более общая с разделителями или неотделенным продолжений, как найти в стандартной схеме или как продолжение монады в Haskell и Scala также может быть использован в качестве побега продолжения.Например, в Racket можно использовать
пусть/ЕС
такой:Многие другие языки также имеют побег продолжения -как конструкции в виде обработки исключений. В Haskell вы также можете использовать один из различных монад ошибка с
foldM
. Потому что они в первую очередь ошибка обработки конструкций с помощью исключения или монады ошибок для скорейшего возвращения обычно культурно неприемлемых и, возможно, довольно медленно.Вы также можете выпадающее из функций высшего порядка для хвостовых вызовов.
При использовании петель, вы входите в следующей итерации автоматически при достижении конца тела цикла. Вы можете ввести следующую итерацию с "продолжить" и выходим из цикла с
перерыв
(иливозвращение
). При использовании хвостовых вызовов (или Clojure'петли с `` конструкцию, которая имитирует хвостовая рекурсия), вы всегда должны явно вызвать для ввода следующей итерации. Чтобы остановить зацикливание, вы просто не'т сделать рекурсивный вызов, но дают значение:Замысловатые часть петли. Давайте начнем с этого. Цикл обычно преобразуются в функциональном стиле, выражая итерации с помощью одной функции. Итерации-это преобразование переменной цикла.
Здесь представлена функциональная осуществления общего цикла :
Он берет (начальное значение переменной цикла, функцию, которая выражает одну итерацию [на переменную цикла]) (условие продолжения цикла).
Ваш пример использует цикл по массиву, который также рвется. Эта возможность в вашем императивный язык, запеченный в сам язык. В функциональном программировании такая возможность обычно реализуется на уровне библиотек. Вот возможная реализация
В нем :
Я использую ((Валь, next_pos)) пара, в котором содержится переменная цикла видна снаружи и позиции в массиве, в котором эта функция скрывает.
Функция итерации является немного более сложным, чем в общем цикле, эта версия позволяет использовать текущий элемент массива. [В карри форма.]
Такие функции обычно назвал"сложить " и;.
Я поставил на "Л" в название, чтобы указать, что накопление элементов массива выполняется в лево-ассоциативной основе; имитировать привычки императивное Программирование языки, чтобы перебрать массив от низкого до высокого показателя.
Я поставил на "с" в название, чтобы указать, что эта версия створка принимает условие, которое определяет, будет ли и когда цикл должен быть остановлен рано.
Конечно, такие полезные функции могут быть легко доступны в базовой библиотеки поставляются с используется функциональный язык программирования. Я написал их здесь для демонстрации.
Теперь у нас есть все инструменты, которые есть в языке, в повелительном случае, мы можем перейти к реализации специфической функциональности вашему примеру.
Переменная в цикле-это пара ('ответить' логический, который кодирует ли продолжать).
Обратите внимание, что я использовал новый "по переменной" - а 'new_answer'. Это потому, что в функциональном программировании не может изменить значение уже инициализированного "и переменной, что". Я не беспокоюсь о производительности, компилятор может сделать для повторного использования памяти 'ответить' и#39;new_answer' через жизнь-время анализа, если он думает, что является более эффективным.
Включение этого в наши функции петли разработали ранее :
на "массив" и вот-имя модуля, которая экспортирует функцию foldlc это.
на "кулак и", "и второй" остановись для функции, которая возвращает первый, второй компонент пары параметр
В этом случае на "Точка-на свободе" стиль улучшает удобочитаемость реализации doSomeCalc:
(>>>) является функцией состава :
(>>>) : (а -> б) -> (б> в) -> (а -> с)
Это то же самое, только в "Башня" и параметр оставить с обеих сторон в определяющие уравнения.
Одна последняя вещь : проверка в случае (массив == значение null). В лучше разработаны языки программирования, но даже в плохо продуманный язык с некоторыми основными дисциплина одна, а использует тип, чтобы выразить небытие. Это не имеет много общего с функциональным программированием, что вопрос в конечном итоге, таким образом, я не бороться с ним.
Во-первых, переписать петля слегка, такой, что каждой итерации цикла либо рано-выходы, или мутирует "ответ" ровно один раз:
Это должно быть ясно, что поведение этой версии точно так же, как и раньше, но теперь это'ы гораздо проще преобразовать в рекурсивный стиль. Здесь'с прямым переводом на Haskell:
Теперь это'ы чисто функциональное, но мы можем улучшить его с обеих эффективности и точки зрения читабельности, используя складки вместо явной рекурсии:
В этом контексте, "влево" рано-выходы с его значением, и "право" продолжает рекурсии с его значением.
Это сейчас можно было бы упростить немного больше, как это:
Это лучше как финальная кода на Haskell, но это's теперь немного менее ясно, как это карты обратно в исходный Java.