В Java вот является возможный хвост рекурсивной реализацией функции Fibonacci:
public int tailRecursive(final int n) {
if (n <= 2)
return 1;
return tailRecursiveAux(n, 1, 1);
}
private int tailRecursiveAux(int n, int iter, int acc) {
if (iter == n)
return acc;
return tailRecursiveAux(n, ++iter, acc + iter);
}
Контраст это со стандартной рекурсивной реализацией:
public int recursive(final int n) {
if (n <= 2)
return 1;
return recursive(n - 1) + recursive(n - 2);
}
Я следовал принципам следующих статей:
В конечном итоге я применил подход Самека. Было очень полезно просто создать макросы для REQUIRE, ENSURE, CHECK и INVARIANT (на основе существующего макроса assert
). Конечно, это не так хорошо, как поддержка родного языка, но в любом случае она позволяет вам получить большую часть практической ценности от этой техники.
Что касается библиотек, я не думаю, что их использование окупается, потому что одно важное значение Механизма утверждений является его простота.
О различиях между отладочным и производственным кодом см. Когда утверждения должны оставаться в рабочем коде? .
Самый простой?
Утверждайте операторы в начале вашей функции для проверки ваших требований. Утверждайте утверждения в конце вашей функции для проверки ваших результатов.
Да, это грубая, это небольшая система, но ее простота делает ее универсальной и портативной.
Некоторые шаблоны проектирования, такие как невиртуальный интерфейс , делают естественным создание предварительных и последующих условий для данного метода:
#include <cassert>
class Car {
virtual bool engine_running_impl() = 0;
virtual void stop_impl() = 0;
virtual void start_impl() = 0;
public:
bool engine_running() {
return engine_running_impl();
}
void stop() {
assert(engine_running());
stop_impl();
assert(! engine_running());
}
void start()
{
assert(! engine_running());
start_impl();
assert(engine_running());
}
}
class CarImpl : public Car {
bool engine_running_impl() {
/* ... */
}
void stop_impl() {
/* ... */
}
void start_impl() {
/* ... */
}
}