gifts2017

Фортран - 77

Опубликовал Александр Рытов (Арчибальд) в раздел Программирование - Практика программирования

Вычисляем формулы на ходу

В продолжение http://infostart.ru/public/18898/ и в ответ на http://infostart.ru/forum/forum14/topic27699/messages/

В простейшем варианте просто вычисляется арифметическое выражение, которое может содержать наряду с числами-литералами также переменные глобального контекста и имена констант, помеченные префиксом "#"
 Можно вести вычисления в контексте выбранного документа. Для реквизитов документа следует использовать префикс "$". При выборе документа дается напоминание о том, какие реквизиты имеются.
 Результат вычисления первой формулы в выражениях обозначается как @Рез1. Можно его записать в какой-нибудь реквизит шапки документа.
 Пользователям не давать! "Защита от дурака" отсутствует!

30.10.2009 Нашел близкую работу http://infostart.ru/public/15862/

Скачать файлы

Наименование Файл Версия Размер Кол. Скачив.
Обработка
.ert 48,50Kb
30.10.09
93
.ert 48,50Kb 93 Скачать

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Александр Рытов (Арчибальд) 13.10.09 14:06
2. Ярослав Радкевич (WKBAPKA) 13.10.09 18:27
не у меня ли случайно сперли этот креативчик из обработки "Финансы и анализ". Там вычисления построены тоже по такому принципу? ;)
3. Александр Шишкин (Шёпот теней) 13.10.09 20:25
ты где тАААкие крАсивые авАтарки ТЫришь ...?

... вот ...
4. Игорь Исхаков (Ish_2) 13.10.09 20:46
(2) Дай Бог памяти , фирма "Камин" в своих поделках использовала вычисление формул через "Шаблон". Тоже сперли ?

(0) Арчибальд, разработки не понял. Ёпрста на тебя нет.
5. Ярослав Радкевич (WKBAPKA) 13.10.09 22:10
фишка не в самом использовании функции Шаблон(), а в использование списка значений для вычислений сложных формул с использованием промежуточных переменных!
6. Евгений Долиновский (Dolly_EV) 14.10.09 05:38
офф-топ: Я когда-то давно-давно таким цветом формы отчетов красил))) ностальгия..
7. Александр Рытов (Арчибальд) 14.10.09 07:42
(2) По календарю не получается. Финансы и анализ - 21.02.09, Вычисляем формулы - 29.01.09.
(4) Это заготовка/идея для разработчика. На форуме возник вопрос (ссылка в описании), я и нарисовал.
Несколько лет назад я писал конфигурацию по заготовке сырья, в том числе, давальческого. Уперся в то, что формулы раздела сырья зависят не только от времени, качества, и т.п., но и меняются в течение сезона. Сначала я завел каталог текстовых модулей документа. Ну, а позже добрался до хранения формул в периодических текстовых константах.
8. Александр Рытов (Арчибальд) 14.10.09 07:44
(3) Жму в ворде на вставить картинку :idea:
Шёпот теней; +1 Ответить
9. Игорь Исхаков (Ish_2) 14.10.09 08:25
Три трудных вопроса :
- зачем разноцветье в обработке ?
- причем тут Фортран 77 ?
- почему не обрабатываются ошибочные ситуации ?
10. Александр Рытов (Арчибальд) 14.10.09 08:40
(9) Три простых ответа:
- Разноцветье минимально, оно должно присутствовать для дистанцирования от 1С;
- ФОРмульный "ТРАНслятор" для платформы 7.7.
- потому что это заготовка для разработчика, а не продажный продукт. Ну, а предупрежден (см. описание) - значит, вооружен.
11. Игорь Исхаков (Ish_2) 14.10.09 09:04
(10) Силён, что сказать. Мне тебя не одолеть .
Куда подевался Ёпрст ?!
12. Ярослав Радкевич (WKBAPKA) 14.10.09 10:08
2(7): :D Вы его хоть смотрели? пасатрите на копирайты, очень удивитесь... этой разработке без малого 7 лет ;)
13. Ярослав Радкевич (WKBAPKA) 14.10.09 10:10
Финансы и анализ v.1.01, © Компания "Бизнес консалтинг системз", 2000-2009
14. Ярослав Радкевич (WKBAPKA) 14.10.09 10:12
Функция ВычислитьЯчейку(Таб,адрес,старТаб=0,старАдрес=0)
	Перем ТабПеременных;
	Перем Контроль;
	Перем сЗначения;
	Перем темпглТабПеременных;
	Перем темпглПеременная;
	БИ = СоздатьОбъект("БухгалтерскиеИтоги");
	сЗначения = СоздатьОбъект("СписокЗначений");
// Создадим таблицу локальных переменных	
	ТабПеременных = СоздатьОбъект("ТаблицаЗначений");
// Инициализируем таблицу локальных переменных	
	ТабПеременных.НоваяКолонка("Идентификатор");
	ТабПеременных.НоваяКолонка("Значение");
	ТекстВыражения = Таб.Область(адрес).Текст;
// Проверим, из скольки строк состоит наше выражение	
	Если СтрКоличествоСтрок(ТекстВыражения) > 1 Тогда
		стр = СтрПолучитьСтроку(ТекстВыражения,1);
	Иначе
		стр = ТекстВыражения;
	КонецЕсли;	
	глобПоз = 1; // Глобальный указатель в потоке лексем
//	Пропустим лидирующие пробелы перед строкой
	Пока КодСимв(СРЕД(стр,глобПоз,1)) = 32 Цикл
		глобПоз = глобПоз + 1;
	КонецЦикла;		
// Проверим, это обычный текст или выражение	
	//Если СРЕД(стр,глобПоз,1) <> "<" Тогда		
	//	Возврат ТекстВыражения;
	//КонецЕсли;	
	Если ((СРЕД(стр,глобПоз,1) <> "=") и (СРЕД(стр,глобПоз,1) <> "<")) Тогда		
		Возврат ТекстВыражения;
	КонецЕсли;	
// Сканируем текст		
	Для инд = 1 по СтрКоличествоСтрок(ТекстВыражения) Цикл				
		Если флагПрервать = 1 Тогда
			флагПрервать = 0;
			Прервать;
		КонецЕсли;	
		стр = СтрПолучитьСтроку(ТекстВыражения,инд);		
		глобПоз =1; текСтрока = инд; 		
// Проверим строку на пустое значение. Может быть это просто интервал между строками
		Если ПустоеЗначение(стр) = 1 Тогда
			Продолжить; // Перейдем к следующей строке
		КонецЕсли;	
		
//	Пропустим лидирующие пробелы перед началом выражения
		Пока КодСимв(СРЕД(стр,глобПоз,1)) = 32 Цикл
			глобПоз = глобПоз + 1;
		КонецЦикла;				
// Если инд = 1 (т.е. первая строка) удалим ненавистный нам символ "<" вместе с его пробелами
		Если инд = 1 Тогда
			стр = СРЕД(стр,глобПоз+1,СтрДлина(стр));
			глобПоз = 1;
		КонецЕсли;			
		глСтр = инд;
// Попробуем найти конец выражения
		конецВыр = Найти(стр,";");
		Если конецВыр = 0 Тогда 
			Ошибка(10,Адрес,инд,0); // Не найден конец выражения.Это плохо, выдаем ошибку
			флагИсключительнойСитуации = 1;
			Возврат "Error";
		ИначеЕсли конецВыр = 1 Тогда // Это хорошо. Дальше идет комментарий 	
			Продолжить;
		КонецЕсли;					
		Переменная =""; флгПеременной = 0;  глПеременная =0;
// Проверим, объявляется ли переменная в этом выражении
		позРавно = НайтиСтр(стр,"=");
		Если позРавно <> 0 Тогда
 // Получим имя переменной. Если перед именем переменной !, значит объявляется глобальная переменная
 			Переменная = ПолучитьИмяПеременной(стр,глобПоз,позРавно);
 			глПеременная = Переменная;
 			Если ((Переменная = "Error") и (флагИсключительнойСитуации =1)) Тогда // Упс !!! Ошибка 
				Ошибка(20,Адрес,инд,Переменная); // Не найден конец выражения.Это плохо, выдаем ошибку				
				флагСтоп = 1;
				Возврат "Error";
 			КонецЕсли;	
		КонецЕсли;
// выделяем выражение
		стрВыражение = Сред(стр,глобПоз,(Найти(стр,";")-(глобПоз-1)));
		стрШаблон = "";
// Сканируем выражение
		Для поз1 = 1 по СтрДлина(стрВыражение) Цикл
			символ = НРег(Сред(стрВыражение,поз1,1));
			Если ДопустимыйСимвол(символ) = 4 Тогда // игнорируем пробел
				Продолжить;
			КонецЕсли;	
			Если СпециальныйСимвол(Символ) = 1 Тогда // Локальная переменная
// Выделим нашу переменную, проверим по таблице переменных, если такой переменной нет, 
// сгенерируем ошибку, если есть получим ее значение и подставим в строку шаблон
				поз1 = поз1 + 1; вырПеременная = "";
				Для поз1 = поз1 по СтрДлина(стрВыражение) Цикл
					символ = НРег(Сред(стрВыражение,поз1,1));					
					ДопустимыйСимвол = ДопустимыйСимвол(символ);
					Если ДопустимыйСимвол = 0 Тогда
						Ошибка(30,Адрес,инд,поз1); // Недопустимый символ						
						флагИсключительнойСитуации = 1;
						Возврат "Error";
					ИначеЕсли ДопустимыйСимвол = 4 Тогда // игнорируем пробел
						Продолжить;	
					ИначеЕсли ДопустимыйСимвол = 1 Тогда // Допустимый символ переменной 	
						вырПеременная = вырПеременная + символ;
					ИначеЕсли (ДопустимыйСимвол = 2) или (ДопустимыйСимвол = 3) Тогда // Нашли допустимый разделитель (+,-,/,*,",") 	 	
						ЗначениеПеременной = ПолучитьПеременную(ТабПеременных,НРег(вырПеременная));
						Если ЗначениеПеременной = "Error" Тогда // Нет такой переменной в таблице переменных
							Ошибка(40,Адрес,инд,поз1,вырПеременная); 
							флагИсключительнойСитуации = 1;
							Возврат "Error";
						Иначе // Есть такая переменная. Подставляем ее значение в шаблон и продолжаем с точки после разделителя
							Если (символ = ";") Тогда 
//							или (символ = ">") 
								символ = "";
							КонецЕсли;		
							типПеременной = ПолучитьТип(ЗначениеПеременной); // Агрегатный тип данных
							Если типПеременной <> 100 Тогда
								Если типПеременной = 2 Тогда
									ЗначениеПеременной = Симв(34) + ЗначениеПеременной + Симв(34);
								ИначеЕсли типПеременной = 1 Тогда // Если число	
									Если Число(значениеПеременной) < 0 Тогда // Если число отрицательное, в скобочки его
										значениеПеременной = "(" + значениеПеременной + ")";
									КонецЕсли;									
								КонецЕсли;	
								стрШаблон = стрШаблон + Строка(ЗначениеПеременной)+символ;					
							Иначе
								сЗначения.ДобавитьЗначение(ЗначениеПеременной);
								ном = сЗначения.РазмерСписка();
								стрШаблон = стрШаблон + "сЗначения.ПолучитьЗначение(" + Строка(ном) + ")" + символ; 
							КонецЕсли;	
							Прервать;
						КонецЕсли;	
					КонецЕсли;					
				КонецЦикла;					
			ИначеЕсли СпециальныйСимвол(Символ) = 2 Тогда // Ссылка на ячейку	
// сканируем лексемы помещая их во временную переменную. Если встречаем допустимые разделители, 
// считаем, что во временной переменной содержится адрес ячейки текущей таблицы. Проверяем таблицу ячеек,
// если данной ячейки там нет, передаем на вычисление. Если встречаем точку, значит во временной
// переменной хранится имя таблицы. Продолжаем сканировать адрес
				поз1 = поз1 + 1; имяТаблицы = ""; АдресЯчейки = ""; темпСтр ="";				
				Для поз1 = поз1 по СтрДлина(стрВыражение) + глобпоз Цикл
					символ = НРег(Сред(стрВыражение,поз1,1));										
					ДопустимыйСимвол = ДопустимыйСимвол(символ);					
					Если ДопустимыйСимвол = 1 Тогда  // обычный допустимый символ						
						темпСтр = темпстр + символ;
					ИначеЕсли ДопустимыйСимвол = 4 Тогда  // игнорируем пробел						
						Продолжить;
					ИначеЕсли ДопустимыйСимвол = 2 Тогда // Заносим адрес ячейки и вываливаемся из цикла
						АдресЯчейки = темпСтр;							
						темпстр = "";						
						Прервать;
					ИначеЕсли (ДопустимыйСимвол = 3) и (ПустоеЗначение(имяТаблицы) = 1) Тогда // точка
						ИмяТаблицы = темпСтр;
						темпстр = "";
					Иначе // Толи точка левая, толи символы недопустимые						
						Ошибка(50,Адрес,инд,поз1); 
						флагИсключительнойСитуации = 1;
						Возврат "Error";
					КонецЕсли;	
				КонецЦикла;	
// Попробуем вычилить ячейку				
				Если ПустоеЗначение(имяТаблицы) = 1 Тогда
					значениеЯчейки = НайтиЯчейку(Таб,АдресЯчейки);
// Даже если ячейка вычислена и вней "Error" ошибки не будет					
					Если значениеЯчейки = "Error" Тогда
// Проверим цикличность ссылок. Если стар адрес = нов адрес ошибка						
						Если НРег(АдресЯчейки) = НРег(старАдрес) Тогда 
							Ошибка(70,Адрес,инд,поз1);
							флагИсключительнойСитуации = 1;
							Возврат "Error";					
						КонецЕсли;
						темпглТабПеременных = глТабПеременных;
						темпглПеременная = глПеременная;
						значениеЯчейки = ВычислитьЯчейку(Таб,АдресЯчейки,,Адрес);						
						глТабПеременных = темпглТабПеременных;
						глПеременная = темпглПеременная;
						Если ПустоеЗначение(значениеЯчейки) = 1 Тогда
							значениеЯчейки =0;
						КонецЕсли;	
					Иначе
// Проверим тип возвращаемого результата						
						Если ПустоеЗначение(значениеЯчейки) = 1 Тогда
							значениеЯчейки =0;
						КонецЕсли;							

						Если ПолучитьТип(значениеЯчейки) = 2 Тогда   // Если возвращаемый тип не число
							ЗначениеЯчейки =0;
							//Ошибка(100,Адрес,инд,поз1);
							//флагИсключительнойСитуации = 1;
							//Возврат "Error";					
						КонецЕсли;
					КонецЕсли;	
				Иначе  // имя не пустое. Инициализируем таблицу
					новТаб = СоздатьОбъект("Таблица");
					ПутькТаблице = ПолучитьПутькТаблице(имяТаблицы);	
					Если ПутькТаблице = "Error" Тогда // Нет такой таблице в базе
						Ошибка(60,Адрес,инд,поз1); 
						флагИсключительнойСитуации = 1;
						Возврат "Error";
					КонецЕсли;	
					новТаб.Открыть(ПутькТаблице);	
// Теперь текТаб = текущей рассчитываемой таблице					
					ТекТаб = новТаб;										
					темпглТабПеременных = глТабПеременных;
					темпглПеременная = глПеременная;
					значениеЯчейки = ВычислитьЯчейку(новТаб,АдресЯчейки);
// Теперь текТаб = основной таблице					
					ТекТаб = Таблица;
					глТабПеременных = темпглТабПеременных;
					глПеременная = темпглПеременная;
				КонецЕсли;							
				Если ПустоеЗначение(значениеЯчейки) = 1 Тогда
					значениеЯчейки =0;
				КонецЕсли;							
				
				Если (значениеЯчейки = "Error") Тогда // Упс, ошибка
					Ошибка(0);
					флагИсключительнойСитуации = 1;
					Возврат "Error";
				ИначеЕсли ПолучитьТип(значениеЯчейки) = 2 Тогда   // Если озвращаемый тип не число
					//Сообщить(значениеЯчейки);
					ЗначениеЯчейки =0;
					//Ошибка(100,Адрес,инд,поз1);
					//флагИсключительнойСитуации = 1;
					//Возврат "Error";					
				Иначе
// Проверим, отрицательный ли результат. Если отрицательный, заключаем его в скобки					
					Если СРЕД(значениеЯчейки,1,1) = "-" Тогда
						значениеЯчейки = "(" + значениеЯчейки + ")";
					КонецЕсли;	
				КонецЕсли;
// Добавим полученное значение в таблицу ячеек только по текущей таблице. По внешней не записываем				
				//Если ПустоеЗначение(имяТаблицы) = 1 Тогда				
				//	ДобавитьЗначениеВТаблицуЯчеек(АдресЯчейки,значениеЯчейки);
				//КонецЕсли;	
				Если (символ = ";") Тогда
				//или (символ = ">") Тогда
					символ = "";
				КонецЕсли;		
//				стрШаблон = стрШаблон + НРег(Строка(значениеЯчейки))+символ;					
				стрШаблон = стрШаблон + значениеЯчейки+символ;					
// сканируем дальше							
//				Прервать;				
			ИначеЕсли СпециальныйСимвол(символ) = 3 Тогда // Нашли начало текстовой строки двойные кавычки 	 	
// Сдесь мы не переводим символы в нижний регистр. Получаем как есть	
				стрШаблон = стрШаблон + символ;						
				поз1 = поз1 + 1;						
// Пытаемся найти закрывающиеся кавычки						
				Для поз1= поз1 по СтрДлина(стрВыражение) Цикл
					символ = Сред(стрВыражение,поз1,1);
					Если (КодСимв(символ) = 34) или (КодСимв(символ) = 59) Тогда
						стрШаблон = стрШаблон + символ;						
						Прервать;
					Иначе	
						стрШаблон = стрШаблон + символ;
					конецЕсли;	
				КонецЦикла;	
				Если КодСимв(Сред(стрВыражение,поз1,1))= 59 Тогда 
					Ошибка(110,Адрес,инд,поз1);
					флагИсключительнойСитуации = 1;
					Возврат "Error";
				КонецЕсли;	
			Иначе // игнорируем
				Если (символ = ";")  Тогда
				//или (символ = ">")					
					символ = "";
				КонецЕсли;		
				стрШаблон = стрШаблон + НРег(символ);		
			КонецЕсли;					
		КонецЦикла;	
		глТабПеременных = ТабПеременных;
//		Попытка
			вычисленноеЗначение = Шаблон("[Значение(" + стрШаблон + ")]");
//		Исключение	
//			Сообщить("!!!");
//		КонецПопытки;	
// /Проверим,что нам возвратила функция шаблон. Если то, что мы ей передали, значит ошибка		
		Если Сред(вычисленноеЗначение,1,1) = "[" Тогда
			флагИсключительнойСитуации = 1;
			Ошибка(9999,Адрес,,ОписаниеОшибки());
			флагИсключительнойСитуации = 1;
			Возврат "Error";
		ИначеЕсли вычисленноеЗначение = "Error" Тогда
			флагИсключительнойСитуации = 1;
			Возврат "Error";			
		КонецЕсли;	
//		КонецПопытки;
// Сдесь инициализируем переменюну значением вычисленного выражения
// Проверяем, объявлялась ли в выражении переменная
		//Если ((ПустоеЗначение(Переменная) = 0) и (флгПеременной = 0)) Тогда 
		//	ИнициализацияПеременной(ТабПеременных,НРег(Переменная),вычисленноеЗначение);
		//КонецЕсли;	
	КонецЦикла;	
//	Попробуем определить тип возвращаемого значения

//	Если ПолучитьТип(вычисленноеЗначение) = 1 Тогда // Число 		
//// Поместим значение ячейки в таблицу вычесленных ячеек		
//		УстановитьЗначениеЯчейки(Таб,Адрес,Число(вычисленноеЗначение));
//		Возврат Число(вычисленноеЗначение);
//	Иначе // Строка или пустое значение	
		УстановитьЗначениеЯчейки(Таб,Адрес,вычисленноеЗначение);		
		Возврат вычисленноеЗначение;
	//КонецЕсли;	
КонецФункции	


...Показать Скрыть
Арчибальд; +1 Ответить
15. Ярослав Радкевич (WKBAPKA) 14.10.09 10:13
за код сильно не ругайте, знаю, что примитив, как никак писал в 2000 году
16. Ярослав Радкевич (WKBAPKA) 14.10.09 10:15
							Иначе
								сЗначения.ДобавитьЗначение(ЗначениеПеременной);
								ном = сЗначения.РазмерСписка();
								стрШаблон = стрШаблон + "сЗначения.ПолучитьЗначение(" + Строка(ном) + ")" + символ; 


...Показать Скрыть
17. Ярослав Радкевич (WKBAPKA) 14.10.09 10:15
18. Александр Рытов (Арчибальд) 14.10.09 10:55
(14-17) Я же не говорю, что идея у меня украдена :D Сравнил просто даты публикации на Инфостарте.
А если по делу, механизм этот (подстановка в формулу через СЗ + подмена контекста вычислений) весьма плодотворен. Я потому его и выложил в "чистом", так сказать виде - простой пример применения.
19. Ярослав Радкевич (WKBAPKA) 14.10.09 11:18
2(18): я шучу конечно, это я из вредности...
20. Ярослав Радкевич (WKBAPKA) 14.10.09 11:19
кстати, выложенная функция вычисляет целые подпрограммы, т.к. работает рекурсивно... там еще переменные используются...
21. Александр Рытов (Арчибальд) 14.10.09 11:27
22. Ярослав Радкевич (WKBAPKA) 14.10.09 19:36
этот код можно сократить, прежде всего на алгоритме группировки лексем в токены, что я не использовал раньше. С токенами значительно проще работать.
23. Ярослав Радкевич (WKBAPKA) 14.10.09 19:40
я бы вообще делал бы так:
1. считываем выражение, кажды символ (лексема) группируем в токен по правилам нашего языка. Формируем поток токенов в виде таблицы значений. При этом каждый токен помечаем, где у нас переменная, где разделить ( +, -, (,) и т.п.), где число
2. После чего можно провести предварительный синтаксический анализ каждого токена. Например, выделить макроподстановки и вычислить их.
3. Передать результат в Шаблон

Код получиться очень компактным и достаточно прозрачным.
24. Roman Biblbox (mr zafod) 14.10.09 22:03
:D Calculator = Новый COMObject("MathCAD.Application")
25. Ярослав Радкевич (WKBAPKA) 14.10.09 22:20
2(24): это не интересно ;) да и контекст передать нет возможности
26. Сергей (Che) Коцюра (CheBurator) 15.10.09 02:01
а не быстрее в модуль залезть и руками написать что надо...? тем более, что "пользователям он не надо"...???
27. Александр Рытов (Арчибальд) 15.10.09 07:44
(26) Не быстрее. Я свою ситуацию в 7 посте описал. Конечно, формулу в периодической константе я сам правлю, но от этого она не перестает быть периодической. Можно, конечно, залезать в модуль документа каждый раз и добавлять кусок кода типа ИначеЕсли...
28. Ярослав Радкевич (WKBAPKA) 15.10.09 13:12
В итоге нет необходимости помечать макроподстановки. Т.е., имеем на входе:

Цена * Количество * 1,2

На выходе:

Цена - переменная
* - Разделитель
Количество - переменная
* - разделитель
1,2 - число

Обходим эту таблицу, пытаемся вычислить переменные, получаем:

10
*
20
*
1.2

И уже эту строку передаем в Шаблон
Арчибальд; +1 Ответить 1
29. Александр Рытов (Арчибальд) 15.10.09 13:27
(28) Это так. К тому же и отловить некорректности можно хотя бы частично.
У меня дополнительная практическая задача была - ограничить одну формулу по длине хотя бы двумя строчками, поэтому я и ввел метасимволы как сокращения для "Константа." и "Контекст."
30. Ярослав Радкевич (WKBAPKA) 15.10.09 14:26
2(29): в этом случае без таких метасимволов не обойдешся, согласен.
31. remi (janber) 21.10.09 09:59
Все это очень хорошо, но в 21 веке руками вписывать в формулу имена переменных или функций как-то старомодно. :) Я постоянно работаю с подобным Фортраном и умучился. Может подскажете, нет ли способа в режиме "Предприятие" задействовать "Синтакс-помощник" (Типовые операции не предлагать!) или OpenConf ?
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа