Вы можете сделать это полностью, и во многих случаях возможность получить предварительные / последующие условия может стоить потери скорости, если вы не используете специальную форму loop / recur.
Специальная форма recur не сделает этого для вас, потому что на самом деле это не вызов функции. Вы можете создать свою собственную функцию-оболочку, которая выполняет проверку границ в предварительных и последующих условиях после выполнения всего кода, или вы можете использовать встроенную функцию trampoline
, сэкономить немного усилий и проверять условия на каждой итерации (вам нужно решить, хотите ли вы этого)
Вы можете превратить это в рекурсивную функцию без перебора стека:
(defn countup [x]
{:pre [(>= x 0)]
:post [(or (ifn? %) (>= % 0))]}
(if (< x 1000000)
#(countup (inc x))
x))
(trampoline (countup 0))
1000000
Это изменяет условие публикации, чтобы игнорировать промежуточные случаи (где он возвращает функцию для запуска далее) и проверять только конечный результат.
Идея, лежащая в основе trampolining, состоит в том, чтобы избежать перебора стека, заставляя каждую итерацию функции возвращать вызов следующей функции, а не вызывать ее напрямую. таким образом используются два кадра стека (один для батута и один для текущего шага)