Вычисление констант препроцессором
Несмотря на все старания, я не могу запомнить число Фибоначчи. Что-то типа 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
См. также Вычисление функций во время компиляции