Выполнить функцию до выполнения определенного условия - PullRequest
8 голосов
/ 12 марта 2011

Я хочу многократно применять некоторую функцию к некоторому состоянию, пока условие не будет выполнено.

Функция f принимает состояние, изменяет его и возвращает его.Снова примените f к возвращенному состоянию и т. Д.

Я думаю, что это сработает.

(first (filter pred (iterate f x)))

Но это немного уродливо.Кроме того, потребление памяти не является идеальным, поскольку итератор будет вынужден оценивать и сохранять промежуточные состояния до тех пор, пока не будет возвращено состояние, в котором сохраняется pred, и в этот момент промежуточные состояния следует собирать мусором.

Я знаю, что вы можете написать простую рекурсивную функцию:

(loop [f x p] (if (p x) x (recur f (f x) p))

Но я ищу функцию библиотеки ядра (или некоторую комбинацию функций), которая делает то же самое с тем жеэффективность памяти.

Ответы [ 6 ]

5 голосов
/ 12 марта 2011

Что вам действительно нужно, так это take-while :

take-while
function

Usage: (take-while pred coll)
Returns a lazy sequence of successive items from coll while
(pred item) returns true. pred must be free of side-effects.

EDIT

Способом использования функций более высокого порядка для достижения желаемого результата может быть:оберните вашу функцию во что-то, что будет использовано trampoline, а именно, функцию, которая будет либо возвращать конечный результат, либо другую функцию, которая выполнит следующий шаг.Вот код:

(defn iterable [f]            ; wraps your function
  (fn step [pred x]           ; returns a new function which will accept the predicate
    (let [y (f x)]            ; calculate the current step result
      (if (pred y)            ; recursion stop condition
        (fn [] (step pred y)) ; then: return a new fn for trampoline, operates on y
        y))))                 ; else: return a value to exit the trampoline

Итеративное выполнение будет выглядеть следующим образом:

(trampoline (iterable dec) pos? 10)
4 голосов
/ 12 марта 2011

Не уверен, что вы подразумеваете под iterator - вы используете его, как если бы это было iterate, и я просто хочу быть уверен, что вы это имеете в виду. В любом случае, ваше решение выглядит хорошо для меня и совсем не безобразно. И память тоже не проблема: iterate может свободно выбрасывать промежуточные результаты всякий раз, когда это удобно, потому что вы не сохраняете никаких ссылок на них, просто вызываете filter для них «потоковым» способом.

3 голосов
/ 07 февраля 2013

Как насчет drop-while

(first (drop-while (comp not pred) (iterate f x))
3 голосов
/ 12 марта 2011

Я думаю, вы должны просто сделать свой цикл простой рекурсивной функцией:

(defn do-until [f x p]
  (if (p x) x (recur f (f x) p)))

(do-until inc 0 #(> % 10)) ; => 11
2 голосов
/ 12 марта 2011

Я не думаю, что есть основная функция, которая делает это точно и эффективно. Следовательно, я бы сделал это с помощью цикла / повторения следующим образом:

(loop [x initial-value]
  (if (pred x) x (recur (f x))))

Loop / recur очень эффективен, поскольку не требует дополнительного хранилища и реализован в виде простого цикла в JVM.

Если вы собираетесь делать это много, то вы всегда можете инкапсулировать шаблон в макрос.

0 голосов
/ 12 марта 2011

Похоже, что вы хотите макрос while.

http://richhickey.github.com/clojure/clojure.core-api.html#clojure.core/while

Использование: (во время теста и тела) Повторно выполняет тело, в то время как тестовое выражение истинно предполагающие Некоторый побочный эффект приведет к тому, что тест станет ложным / ноль. Возвращает ноль

В немного ином случае использования макрос for поддерживает параметры: when и: while.

http://richhickey.github.com/clojure/clojure.core-api.html#clojure.core/for

Использование: (для seq-exprs body-expr) Понимание списка. Принимает вектор из одного или нескольких пары привязки-формы / коллекции-выражения, каждая из которых следует за нулем или более модификаторы, и возвращает ленивую последовательность вычислений expr. Коллекции повторяются вложенным способом, самый быстрый справа, и вложенные выражения-ссылки могут ссылаться на привязки, созданные в предыдущих связывание-формы. Поддерживаются следующие модификаторы:: let [binding-form expr ...], : во время теста:: во время теста.

(принять 100 (для [x (диапазон 100000000) y (диапазон 1000000): while (

...