Как НУЖНО оформлять исходные тексты программ::

Есть много разных дисциплин, в которых на практических/лабораторных занятиях студентам нужно написать ту или иную программу на том или ином языке программирования. И делают они это, согласно нашим наблюдениям, по принципу "абыкакнитьработало", совершенно игнорируя элементарные правила оформления текстов своих программ. На замечания преподавателей, как правило, не реагируют, особенно на младших курсах. К пятому курсу умнеют, начинают понимать. Впрочем, долой эмоции. Итак, тексты программ. Исходный текст программы - это, по-большому счету, такой же текст, как стихотворение, рассказ, web-страничка. Посмотрите на этот же абзац, только слегка покореженный:

 Есть много разных дисциплин , в которых на 
	практических/лабораторных заня
	 тиях
студентам нужно написать ту или иную программу на том или 
ином языке программирования .
 И делают они это ,
	 согласно нашим наблюдениям, по принципу " аб
		 ы какнить работало",
совершенно игнорируя э
 лементарные правила офо
 рмления текстов своих програ
			 мм. 
На замечания преподавателей, как правило, не реагируют ,особе
 нно 
на младших курсах.К пятому курсу умнеют,начинают понимать.Впр
очем, долой эмоции. Итак,тексты
программ.
 Исходный текст программы-это,по
	большому счету,такой же текст,как стихо творение,рассказ, web -
	страничка. Посмотрите на этот же абзац, только
					слегка покоре
		женный:

Не правда ли, читать и ПОНИМАТЬ такое не очень удобно и приятно? Ну так вот нам, преподавателям, тоже очень неудобно читать и ПОНИМАТЬ Ваши программы, написанные в стиле "как руки на клаву легли", особенно с экрана, и особенно-особенно-особенно, когда Вы, отчаявшись найти ошибку, из-за которой Ваша программа не работает, обращаетесь за помощью к нам. "Ой, а посмотрите, почему у меня тут эта не работает?". Бывает такое? Бывает. А я вот скажу, что отчасти причиной ошибки может быть как раз то, что программа совершенно безобразно отформатирована и Вы просто НЕ ВИДИТЕ дурацкого ляпсуса типа "скобочка не там стоит".

Так вот, Уважаемые студенты! Если Вы хотите слегка облегчить жизнь себе и преподавателям, то Вы ДОЛЖНЫ форматировать исходные тексты Ваших программ так, как изложено ниже. А преподавателям я настоятельно советую ОТКАЗЫВАТЬСЯ от поисков ошибок в программах, если они не удовлетворяют нижеприводимым требованиям.

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

Итак, начнем. Прежде всего, разрешите напомнить, что стандартный размер экрана - 80x25 символов (знакомест). Это не чья-то прихоть, именно такие размеры наиболее комфортны для восприятия. Все, наверняка, натыкались на web-странички, для чтения которых надо чуть не голову поворачивать слева направо при чтении строки. 80 символов в строке максимум, если больше, читать неудобно. К сведению: в полиграфии есть правило, согласно которому в строке должно быть не более 66-ти (вроде бы) символов. Теперь приступим непосредственно к правилам оформления исходных текстов программ.

Правило #1, про количество операторов на одной строчке

Количество операторов на строчке должно быть равно 1.

НЕправильно:

		a = b; do while (0); printf("dah-bah-dee\n"); 

Правильно:

		a = b; 
		do while (0); 
		printf("dah-bah-dee\n"); 

Правило #2, про горизонтальные отступы

Все операторы, входящие в составной оператор (напоминаю, составной оператор - это все, что между begin и end в языке программирования Паскаль и между { } в языке программирования C) должны быть сдвинуты вправо. Напоминаю, есть такая клавиша на клавиатуре, TAB, вот ей и двигайте, не надо лепить пробелы. Кроме того, размер отступа должен быть 8 (ВОСЕМЬ) символов. Настройте редактор, если он меньше.

НЕправильно:

			if ( a < b ) {
			j = m;
			i = l;
			for (k = 0; k < N; k++) {
			c[k] += (m-l);
			u[k-j] = 2*j;
			if ( l > a ) {
			break;
			};
			};
			printf("ooo, yeahhhh!!!!\n");
			};

Правильно:

			if ( a < b ) {
	
				j = m;
				i = l;
		
				for (k = 0; k < N; k++) {
				
					c[k] += (m-l);
					u[k-j] = 2*j;
			
					if ( l > a ) {
						break;
					};
				};
		
				printf("ooo, yeahhhh!!!!\n");
			};

В том, что размер отступа ДОЛЖЕН БЫТЬ 8 СИМВОЛОВ, есть простой смысл и практическая польза. Именно, если текст у Вас начинает залезать вправо за границу экрана (за границу НОРМАЛЬНОГО экрана шириной 80 символов, а не 180!!!), то, стало быть, у Вас слишком велика вложенность блоков. Терпимая вложенность - от силы 3. Если она у Вас больше, это означает, что Вы плохо продумали алгоритм и структуру программы и Вам надо это дело переосмыслить, ввести какие-то функции, заменив какие-то большие части кода их вызовами, переделать алгоритм и т.п. А укажет Вам на это уехавший далеко вправо текст, если у Вас табуляция такая, какая должна быть. А должна она быть, напоминаю, 8 символов.

Правило #3, про операторные скобочки

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

НЕправильно:

			for (;;) {
				printf("тра-ля-ля\n");
				}; 
	
			for j:=0 to N do begin
				WriteLn('тра-ля-ля');
				end;
	

Правильно:

			for (;;) {
				printf("тра-ля-ля\n");
			}; 
	
			for j:=0 to N do begin
				WriteLn('тра-ля-ля');
			end;

Допускается такой вариант:

			for (;;) 
			{
				printf("тра-ля-ля\n");
			}; 
	
			for j:=0 to N do 
			begin
				WriteLn('тра-ля-ля');
			end;

Предыдущий, однако, предпочтительней, так как никакого смысла в размещении открывающей операторной скобки на одной линии по вертикали с закрывающей, кроме удлинения исходного текста в вертикальном направлении, нет.

Последний вариант следует использовать для подпрограмм, то биш процедур и функций:

НЕправильно:

			int my_favorite_function(void) {
				return 1;
			}	
	
			function MyFavoriteFunction:boolean; begin
				MyFavoriteFunction := TRUE;
			end;

Правильно:

			int my_favorite_function(void)
			{
				return 1;
			}	
	
			function MyFavoriteFunction:boolean;
			begin
				MyFavoriteFunction := TRUE;
			end;

Правила 1-2-3 соблюдаются почти АВТОМАТИЧЕСКИ, если набирать текст программы не так, как это делает человек, 2 дня назад впервые увидевший клавиатуру, а так, как сейчас будет сказано. Начинающие обычно набирают текст последовательно, и поэтому (ИМЕННО ПОЭТОМУ!!!) а) он у них получается кривой и б) они постоянно забывают закрыть блок или закрывают его не там, где надо. Для того, чтобы избежать трат времени на исправление последствий такой забывчивости, текст программы нужно набирать НЕпоследовательно. К этому располагает сама грамматика и синтаксис почти любого языка программирования. Всегда есть ПАРНЫЕ символы, скобочки там всевозможные, составные операторы опять же. Если написали begin, то почему бы сразу не написать end? Если набрали открывающую скобочку, то почему бы сразу не набрать закрывающую, а потом не вернуться назад? Сейчас на примере распишу, как надо набирать.

			for (i = 0; i < N; i++) {
				/* тело цикла */
			}

Вот цикл. Набираем так: набрали "for", пробел, нажали левый Shift, набрали "(", сразу (!!!) за ней ")", Shift отпустили, пробел, опять нажали Shift, набрали "{" и опять сразу (!!!) "}", она ведь тут же, рядом на клавиатуре, теперь стрелочка назад и Enter. Если так набирать, то Вы просто не сможете нарушить правило #3. Далее: возвращаемся внутрь круглых скобочек и только теперь начинаем думать, а по какой же переменной мы хотели сделать цикл. Ставим два раза точку с запятой (можно и раньше было поставить, когда скобочки ставили) и вписываем чего надо. Дальше - End, Enter, TAB (правило #2!!!) и начинаем думать, что же мы хотели написать в теле цикла. Если наберете таким образом несколько конструкций типа for, while, repeat, if и прочая и прочая, то Вы будете потом недоумевать, а как еще-то можно набирать??? В противном случае Вы так и будете писать программы, которые невозможно читать, так и будете безнадежно путаться в скобочках и т.д. и т.п. ...

Правило #4, про горизонтальные пробелы

В конце строки пробелов быть не должно!!! Ну незачем там они. Пробелы ОБЯЗАТЕЛЬНЫ после запятой, как в обычном тексте. Пробелы полезны вокруг знака какой-нибудь операции.

НЕправильно:

			for(k=NUM;k;k-){
				int tmp;
				tmp=some_func(k,arg2,arg3);
				printf("%d\n",tmp);
			}
	

Правильно:

			for (k = NUM; k; k-) {
				int tmp;
				tmp = some_func(k, arg2, arg3);
				printf("%d\n", tmp);
			}

Правило #5, про вертикальные пробелы (пустые строки)

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

НЕправильно:

			int func_1(void)
			{ return 1;
			}
			int func_1(void)
			{ return 2;
			}
			int main(void) {
			LIST *l = get_head();
			double a, b;
			printf("Hello.\n");
			for (l;l->next;l=l->next)
				printf("%s\n", l->name);
			};
			printf("Bye.\n");
			}

Правильно:

			int func_1(void)
			{
				return 1;
			}

int func_1(void) { return 2; }

int main(void) { LIST *l = get_head(); double a, b; printf("Hello.\n"); for (l; l->next; l = l->next) printf("%s\n", l->name); printf("Bye.\n"); }

Правило #6, про объявления переменных

Тип у переменной писать для КАЖДОЙ из них. На одной строчке - одна переменная.

НЕправильно:

			int a,b,c;
			float *q,w,u,z,tmp,ttt,qqq,aaa,bbb;
			........
			a,b,c,d,ttt,test,tt0: integer;
			q,w,u,z,ppp,ddd,ddd0,ddd23: real;
			

Правильно:

			int a;
			int b;
			int c;
			float *q;
			........
			a: integer;
			b: integer;
			c: integer;
			q: real;

Правило #7, про говорящие идентификаторы

Идентификаторы (названия переменных, типов, подпрограмм) должны быть значимыми настолько, чтобы читающий текст программы мог понимать смысл всех переменных и т.д. без присутствия рядом автора. Читающим может оказаться и сам автор ... где-то так через полгодика. И будет мучительно вспоминать, что же означало "ffff_1" и "aaa". Если есть какая-то производная, назовите ее Deriv, Proizvod, но никак не FF.

Правило #8, про инициализацию массивов

Массивы с заранее предопределенными значениями (если их не требуется по заданию вводить с клавиатуры или из файла) инициализировать НЕ операторами присваивания, а с использованием инициализаторов и типизированных констант в языках Си и Паскаль, соответственно.

НЕправильно:

			double matrix[3][3];
			....
			matrix[0][0] = 1.5;
			matrix[0][1] = 2.5;
			......
			
			Matrix : array[1..3, 1..3] of extended;
			......
			Matrix[1,1] := 1.5;
			Matrix[1,2] := 2.5;

Правильно:

			double matrix[3][3] = {
						{1.5, 2.5, ... },
						{...},
						{...},
						};
						
			const
				Matrix: array[1..3, 1..3] of extended = 
						(
							(1.5, 2.5, ...),
							(...),
							(...)
						);

Правило #9, про функции/процедуры

9.1) Функции ДОЛЖНЫ БЫТЬ. Программа без подпрограмм - почти заведомо плохая программа, за исключением программы, выводящей сообщение "Hello, world!" (и то не всегда).
9.2) У функций должны быть параметры. Функция, использующая глобальные переменные - почти наверняка плохая, ибо ситуации, когда внутри функции необходимо использовать глобальную переменную, крайне редки. Подпрограмма, реализующая, например, метод Гаусса для решения систем линейных уравнений и не имеющая параметров - это УЖАСНО. Это как сотовый телефон, с которого можно звонить только на один навсегда заданный номер и на который можно звонить тоже только с одного номера (причем с таксофона).
9.3) Функции должны быть короткими, 2 экрана (50 строк) максимум. Это касается и главной функции программы, main() в языке C или главных begin end. в языке Pascal. Единственное оправдание длинной функции - это наличие в ней оператора множественного выбора с большим числом альтернатив.
9.4) Функции должны выполнять РОВНО ОДНО ДЕЙСТВИЕ. Например, подпрограмма, выполняющая решение системы линейных уравнений, НЕ ДОЛЖНА заниматься считыванием исходных данных, она должна получить их в виде аргументов. Она также не должна выводить результаты куда бы то ни было. Это должна делать другая подпрограмма.

Правило #10, про ошибки и еще кое про что

Если та или иная функция может потенциально завершиться неудачно, это ОБЯЗАТЕЛЬНО следует проверять. Это предохранит Вас и преподавателя(!) от долгих минут тупого созерцания экрана в попытке понять, почему Ваше творение падает на ровном месте либо ведет себя в высшей степени загадочно.

НЕправильно:

		char * buf;
		buf = (char*)malloc(/*много-много байт*/);
		buf[0] = some_value;

Правильно:

		char * buf;
		buf = (char*)malloc(/*много-много байт*/);
		if ( !buf ) {
			perror("нет памяти");
			exit(1);
		};
		buf[0] = some_value;

Если возникшая ошибка такова, что программа/подпрограмма далее выполняться не может, условие проверки на ошибку ЛУЧШЕ писать так, чтобы оно было ИСТИННЫМ, если ошибка ПРОИЗОШЛА, а не наоборот и код строить так, чтобы избегать оборота else в операторе if.

Помните, что из программы/подпрограммы можно выйти в любом месте, не обязательно в конце ее текста.

НЕправильно:

		char * buf;
		buf = (char*)malloc(/*много-много байт*/);
		if ( buf ) {
			/* здесь страница кода */
		} else {
			perror("нет памяти");
		};

Этот else может быть далеко от if, что уменьшает ясность программы.

Правильно:

		char * buf;
		buf = (char*)malloc(/*много-много байт*/);
		if ( !buf ) {
			perror("нет памяти");
			/* выходим */
			exit(BAD_EXIT_CODE) 
			/* или return BAD_RET_VAL; */
		};
		/* в этом месте все хорошо, продолжаем */

Правило #11, про данные

Организация данных в программе - это вопрос не менее важный (а иногда, возможно, даже более важный), чем алгоритмическая структурированность программы. Специально для целей структуризации данных существуют замечательные типы данных, структуры (или записи в терминах языка программирования Паскаль) и классы (в ООП-языках). Если Ваша программа работает с какой-либо сущностью, сия сущность, особенно если она сложная, ДОЛЖНА быть описана в программе посредством структуры или класса, если Вы пишете на каком-то объектно-ориентированном языке. Сложная программа, в которой нет ни одного массива или структуры - это ПЛОХАЯ программа.

В заключение приведу пример простенькой программы (вычисление определенного интеграла методом прямоугольников) в двух вариантах. Первый будет написан с нарушением (насколько это у меня получится) всех указанных выше требований, а второй - с соблюдением оных (опять же, насколько получится :-) ). Обе программы работают и дают одинаковый результат. Однако на первую смотреть не хочется (как я такую гадость смог написать, не понимаю ... чего не сделаешь ради студентов ...). Аналогия: Вам нужно иметь машину, чтобы ездить из дома на работу и обратно. Денег хватает хоть на Порше. Дорога хорошая. Результат (попадание на работу и домой) будет как-то достигнут независимо от того, купили ли Вы подержанную "копейку" (сиденья без обивки, краска облуплена, резина голая и т.д.... но ведь ЕЗДИТ же!!!) или нулёвую Вольво 840. Однако же (при наличии деньгоф) все почему-то предпочитают что-нить ближе к второму варианту, не правда ли? А я вот предпочитаю, чтобы программа не только работала, но еще и выглядела должным образом.

Несогласие с сией точкой зрения карается моим (и надеюсь, не только моим) полным отказом выискивать глюки в Ваших программах, если они написаны не так, как изложено здесь.

НЕправильно:

			#include 
			#include 
			double a=0.2,b=1.0,i,h,x,s;
			int n=50,k;
			void ffa(void)
			{h=(b-a)/n;x=a+h/2;s=0;
			for(k=1;k<=n;k++){s+=sin(x);x+=h;}
			s*=h;} void main(void) { 
			ffa();
			printf("значение=%.8f\n",s);}

Правильно:

	#include 
	#include 

#define INTEGR_FUNCTION(x) sin((x)) #define INTEGR_FUNCTION_STR "sin(x)" #define LOWER_LIMIT 0.2 #define UPPER_LIMIT 1.0 #define INTERVALS 50

typedef struct integr_task { double from; double to; double (*what)(double); /* интегрируемая функция */ int nint; /* число интервалов */ } INTEGR_TASK;

typedef enum integr_error { INTEGR_OK = 0, INTEGR_NO_TASK_OR_RES, INTEGR_NO_FUNC, INTEGR_BAD_LIMITS, INTEGR_BAD_NINT, INTEGR_LAST_ERROR } INTEGR_ERROR;

char *integr_error_str[INTEGR_LAST_ERROR+1] = { "Все хорошо", "Указатель на задание или результат равен NULL", "Указатель на функцию равен NULL", "Верхний предел меньше нижнего", "Неправильное число интервалов", "__INTEGR_LAST_ERROR__" };

INTEGR_ERROR integrate_rect(INTEGR_TASK * task, double * result); #define INTEGRATE integrate_rect

double function(double arg) { return INTEGR_FUNCTION(arg); }

int main(void) {

double integral_val; INTEGR_ERROR error; INTEGR_TASK test_task = { LOWER_LIMIT, UPPER_LIMIT, function, INTERVALS };

error = INTEGRATE(&test_task, &integral_val); if ( error ) { printf("Ошибка в данных (%s).\n", integr_error_str[error]); return 1; }; printf( "\nЗначение определенного интеграла от функции f(x) = %s\nв пределах от %.8f до %.8f: %.8f\n", INTEGR_FUNCTION_STR, LOWER_LIMIT, UPPER_LIMIT, integral_val );

return 0; }

INTEGR_ERROR integrate_rect(INTEGR_TASK * task, double * result) { double x, s, step; int j;

/* проверка на корректность данных */ if ( (!task) // (!result) ) { return INTEGR_NO_TASK_OR_RES; }; if ( !task->what) { return INTEGR_NO_FUNC; }; if ( task->from > task->to ) { return INTEGR_BAD_LIMITS; };

if ( task->nint < 1 ) { return INTEGR_BAD_NINT; };

/* собственно вычисление */ step = (task->to - task->from)/task->nint; x = task->from+step/2.0; s = 0;

for (j = 1; j <= task->nint; j++, s += task->what(x), x += step); s *= step; *result = s; return INTEGR_OK; }

Почувствуйте разницу, что называется. Надеюсь, ТЫ понял. Или поняла.

 


Страница сайта http://silicontaiga.ru
Оригинал находится по адресу http://silicontaiga.ru/home.asp?artId=4763