Germany | Finland | Saint Petersburg | Drive

Вычисление констант препроцессором

Опубликовано в M4

Несмотря на все старания, я не могу запомнить число Фибоначчи. Что-то типа 0.61. Зато помню, как оно вычисляется: (sqrt(5)+1)/2. Приходится лезть в справочник или считать на калькуляторе. Потом присваивать значение переменной и уже переменную использовать в тексте. Особых проблем нет, но в эконом-классе не наливают, в иллюминаторе одни облака и надо чем-то себя занять.

А почему бы не рассчитать все эти константы в момент сборки скрипта, а не на этапе его выполнения? Попробуем. Препроцессор M4 имеет замечательный встроенный макрос eval(). Если ему в качестве параметра подсунуть выражение, препроцессор расширит макрос и результат этого выражения. Пробуем:

  • eval(2+5)     даёт 7
  • eval(100/2)  даёт 50
  • eval(4/3)      даёт 1       упс... Хмурюсь

Математика-то только целочисленная. Жаль.

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

$ans = execute($CmdLineRaw);
if $ans == "" Then
      ConsoleWrite("Error! I do not understand: " & $CmdLineRaw);
Else
      ConsoleWrite($ans);
EndIf

Компилируем его в консольное приложение (я собираю в x64):

@"C:\Program Files (x86)\AutoIt3\Aut2Exe\Aut2exe_x64.exe" /in m4_calc.au3 /out %M4PATH%\m4_calc.exe /console

Пробуем:

  • m4_calc 2+5                      --> 7
  • m4_calc 100/2                   --> 50
  • m4_calc 4/3                       --> 1.33333333333333
  • m4_calc ((sqrt(5)+1)/2      --> 1.61803398874989
  • m4_calc 4*atan(1)             --> 3.14159265358979

Шикарненько.

Макрос для m4, рассчитывающий выражение на этапе сборки:

define(`m4_calc',`esyscmd(`m4_calc.exe $1')')

Так что мы имеем с гуся и зачем нам вся эта головная боль? Кроме тривиальной задачи расчета математических констант можно решать и другие задачки. Например вот как можно ускорить расчет скользящей средней. Берем отсюда расчет средней T3 и слегка его модифицируем:

e3 = EMA(EMA(EMA(array,Periods),Periods),Periods); 
e4 = EMA(e3,Periods);
e5 = EMA(e4,Periods);
e6 = EMA(e5,Periods);
pushdef(`k',0.7)
result = m4_calc(3*k^2 + 3*k^3)*e5 - m4_calc(3*k^3 + 6*k^2 + 3*k)*e4 + m4_calc(1+3*k + 3*k^2 + k^3)*e3 - m4_calc(k^3)*e6;
popdef(`k')

после препроцессора имеем: 

e3 = EMA(EMA(EMA(array,Periods),Periods),Periods); 
e4 = EMA(e3,Periods);
e5 = EMA(e4,Periods);
e6 = EMA(e5,Periods);
result = 4.35*e5 - 8.61*e4 + 5.99*e3 - 0.73*e6;

Значительно лучше. А ведь это выражение просчитывается на каждом тике!

 

 


Можно исправить ошибку компилятора QPILE, описанную в статье Вариант задания числовой константы.

m4_calc(3E10)

очевидно, развернется в

30000000000

См. также Вычисление функций во время компиляции

Добавить комментарий


Библиотека