?

Log in

No account? Create an account
 
 
11 July 2009 @ 09:05 am
Записки программиста.  
Еще одно программистское заблуждение: "хороший" программный код не требует документации.

Я уже писал о некоторых подобных заблуждениях. Скажем о том, что предварительная оптимизация кода это зло или про абсолютный вред использовыние оператора goto. Как правило каждое такое заблуждение является неправльным обобщением совершенно справедливого наблюдения.

Еще одно распространенное заблуждение - это то, что хороший программный код не требует документации. Я, конечно, говорю о внутренней документации - не для пользователей, которые используют продукт как черный (или очень темный :-) ) яшик, а для тех, кто должен этот код понимать - с целью проверки, переделки и.т.д.

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

Но обобщение неправильно.
Тривиальным противоречащим примером может служить любой простой (то есть короткий) нетривиальный алгоритм. Скажем, один из оптимальных O(N*log(N)) алгоритмов сортировки (упорядочения массива):

template
[Error: Irreparable invalid markup ('<class [...] t_,>') in entry. Owner must fix manually. Raw contents below.]

Еще одно программистское заблуждение: "хороший" программный код не требует документации.

Я уже писал о некоторых подобных заблуждениях. Скажем о том, что предварительная оптимизация кода это зло или про абсолютный вред использовыние оператора goto. Как правило каждое такое заблуждение является неправльным обобщением совершенно справедливого наблюдения.

Еще одно распространенное заблуждение - это то, что хороший программный код не требует документации. Я, конечно, говорю о внутренней документации - не для пользователей, которые используют продукт как черный (или очень темный :-) ) яшик, а для тех, кто должен этот код понимать - с целью проверки, переделки и.т.д.

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

Но обобщение неправильно.
Тривиальным противоречащим примером может служить любой простой (то есть короткий) нетривиальный алгоритм. Скажем, один из оптимальных O(N*log(N)) алгоритмов сортировки (упорядочения массива):

template<class T_, class IT_> void Sort_cor_(T_ * a_, IT_ num_, IT_ ind_)
{
for(IT_ i, j; (i = ind_ * 2 + 1) < num_; )
{
if((j = i + 1) == num_)
{
if(!(a_[ind_] < a_[i]))
break;
}
else
{
if(!(a_[ind_] < a_[i]))
{
if(!(a_[ind_] < a_[j]))
break;
i = j;
}
else if(a_[i] < a_[j])
i = j;
}
swap(a_[ind_], a_[i]);
ind_ = i;
}
return;
}

template<class T_, class IT_> void Sort(T_ * a_, IT_ num_)
{
for(IT_ i = num_ / 2 - 1; i-- != 0; Sort_cor_(a_, num_, i));
for(; num_ > 1; Sort_cor_(a_, --num_, (IT_)0))
swap(a_[0], a_[num_ - 1]);
}

Запись подобного алгоритма (программы) весьма коротка. Но для его понимания - если,
конечно, Вы не снакоми с этим алгоритмом и не догадались что это именно эго
имплементация, нужнyа значительно большая информация. Даже, предполагая, что решаемая
задача известна, понять как он работает очень сложно без описания структуры промежуточных
данных и условий, которым эти промежуточные данные удовлетворяют. То есть - кроме
алгоритма, нужно еще и нечто соответствующее математическому доказательству того,
что этот алгоритм выполняет описанную задачу (то есть сортировку массива значений).
С промежуточными определениями и леммами, которые нужны только для доказательства
и которым не соответствуют никакие части программы.

Надо сказать, что есть и формальные математические результаты, подкрепляющие этот факт.
Один из них - это теорема о том, что доказательство правильности программы решающей
некоторую проблемме (сформулированную в форме теоремы конструктивной математики) в общем
случае не проще чем доказательство этой теоремы "с нуля", без использования кода программы.
(слышал я об этом резульнате давно от Н.Н.Непейвода; не уверен его ли этот результат или
кого-нибудь еще).

Другой результат даже более классический - хотя применим с некоторым допущением. Это теорема
о неразрешимости проблемы эквивалентности двух алгоритмов. Натяжка в том, что там имеется в виду
машина Тьюринга с бесконечной памятью, а мы имеем в виду современные компьютеры с конечной,
хотя и большой памятью. Так что формальной неразрешимости в нашем случае, конечно, быть не может.

Напоследок о маленькой хитрости связанной с определением документации. Естественно называть
документацией кода то, что может быть удалено перед выполнением программы, не влияя на ее
результат. Просто текст, даде вставленный в код в форме комментарий, удалить легко. Но
некотороая информация для людей не нужная машине может быть вставлена и в неудаляемые элементы
кода, скажем, типично переменные и функции (как и другие объекты) называть мнемоническими
именами и это часто очень помогает чтению кода. Теоретически можно использовать невероятно
длинные имена переменных и поместить туда страницы текста документации :) А с другой стороны,
можно и удалить все имена переменных, заменив их на V1, V2 и.т.д.

Но это, конечно, софистика. Главное - это то, что существуют случаи, когда документация нужная
для понимания программного кода имеет размер существенно превышающий размер самого кода. Я в таких
случаях обычно помещаю в код или длинный текст в форме комментария или ссылку на соответствующую
статью или учебник.