Составители:
Рубрика:
static double Integrate(Function f, double a, double b, int n)
{
double h = (b-a)/n, sum = 0.0;
for (int i = 0; i < n; i++)
sum += h*f.Eval((i+0.5)*h);
return sum;
}
Для проверки работоспособности алгоритма можно объявить тесто-
вый класс TestFunction, реализующий вычисление функции f(x) = x *
sin(x):
public class TestFunction: Function
{
public override double Eval(double x)
{
return x * Math.Sin(x);
}
}
5.1.2. Представление выражений
В нашем примере пользователь будет вводить выражение с клавиату-
ры, то есть оно будет представлено в виде текстовой строки. Такое пред-
ставление неудобно ни для непосредственного вычисления значения
функции, ни для генерации кода, вычисляющего ее значение. Поэтому
нам понадобится парсер и некоторое представление, в которое этот парсер
будет переводить введенную с клавиатуры текстовую строку.
Детали синтаксического анализа и представления выражений мы
рассматривать не будем: интересующиеся могут обратиться к полным ис-
ходным текстам примера. Скажем лишь, что анализ осуществляется мето-
дом рекурсивного спуска и транслирует выражение в дерево, в узлах кото-
рого расположены объекты, представляющие арифметические операции и
их операнды. Каждый из этих объектов является экземпляром одного из
четырех классов, обозначающих числовые константы, переменные, унар-
ные и бинарные операции. Причем все эти классы наследуют от абстракт-
ного класса Expression:
public abstract class Expression
{
public abstract string GenerateCS();
public abstract void GenerateCIL(ILGenerator il);
public abstract double Evaluate(double x);
}
В классе Expression объявлены три абстрактных метода, которые ка-
ждый класс-наследник реализует по-своему. Метод Evaluate выполняет
Динамическая генерация кода
165
Если сравнить эти два способа, можно придти к выводу, что порож-
дение C#-программы несколько проще, нежели генерация CIL-кода. Од-
нако, наличие в библиотеке классов пространства имен
System.Reflection.Emit позволяет избежать рутинных и трудоемких опера-
ций по работе с физическим представлением метаданных и CIL-кода.
Кроме того, генерация CIL-кода выполняется на порядок быстрее и дает
большую гибкость. Поэтому для программиста, знакомого с набором ин-
струкций CIL, второй способ является более предпочтительным.
В этом разделе мы рассмотрим простой пример программы на языке
C#, выполняющей численное интегрирование функции, которую пользо-
ватель вводит с клавиатуры (то есть интегрируемая функция становится
известной только в процессе выполнения программы). Исходный код
примера приведен в Приложении B. Характерной особенностью задачи
численного интегрирования является необходимость многократного вы-
числения значения функции в разных точках. При этом, так как функция
представлена в виде строки, это вычисление связано со значительными
затратами времени процессора. Таким образом, данная задача по всем
признакам подходит для использования динамической генерации кода.
Мы будет выполнять вычисление значения функции тремя способа-
ми:
1. Без динамической генерации кода (путем непосредственной ин-
терпретации выражения).
2. Путем динамической генерации программы на языке C#.
3. Путем динамической генерации метаданных и CIL-кода.
Затем мы сравним эффективность каждого способа.
5.1.1. Обобщенный алгоритм интегрирования
Для интегрирования функций нам потребуется некое представление
функции, которое бы не зависело от конкретного способа вычисления
значения функции. Идеальным вариантом такого представления является
абстрактный класс Function:
public abstract class Function
{
public abstract double Eval(double x);
}
Объявив такой класс, мы предполагаем, что от него будут наследо-
ваться другие классы, реализующие в методе Eval конкретный способ вы-
числения значения функции в заданной точке.
Имея класс Function, мы можем записать обобщенный алгоритм ин-
тегрирования методом прямоугольников. В качестве параметров этот ал-
горитм принимает объект f, представляющий интегрируемую функцию,
пределы интегрирования a и b, а также количество разбиений n:
164
CIL и системное программирование в Microsoft .NET
164 CIL и системное программирование в Microsoft .NET Динамическая генерация кода 165 Если сравнить эти два способа, можно придти к выводу, что порож- static double Integrate(Function f, double a, double b, int n) дение C#-программы несколько проще, нежели генерация CIL-кода. Од- { нако, наличие в библиотеке классов пространства имен double h = (b-a)/n, sum = 0.0; System.Reflection.Emit позволяет избежать рутинных и трудоемких опера- for (int i = 0; i < n; i++) ций по работе с физическим представлением метаданных и CIL-кода. sum += h*f.Eval((i+0.5)*h); Кроме того, генерация CIL-кода выполняется на порядок быстрее и дает return sum; большую гибкость. Поэтому для программиста, знакомого с набором ин- } струкций CIL, второй способ является более предпочтительным. Для проверки работоспособности алгоритма можно объявить тесто- В этом разделе мы рассмотрим простой пример программы на языке вый класс TestFunction, реализующий вычисление функции f(x) = x * C#, выполняющей численное интегрирование функции, которую пользо- sin(x): ватель вводит с клавиатуры (то есть интегрируемая функция становится public class TestFunction: Function известной только в процессе выполнения программы). Исходный код { примера приведен в Приложении B. Характерной особенностью задачи public override double Eval(double x) численного интегрирования является необходимость многократного вы- { числения значения функции в разных точках. При этом, так как функция return x * Math.Sin(x); представлена в виде строки, это вычисление связано со значительными } затратами времени процессора. Таким образом, данная задача по всем } признакам подходит для использования динамической генерации кода. Мы будет выполнять вычисление значения функции тремя способа- 5.1.2. Представление выражений ми: В нашем примере пользователь будет вводить выражение с клавиату- 1. Без динамической генерации кода (путем непосредственной ин- ры, то есть оно будет представлено в виде текстовой строки. Такое пред- терпретации выражения). ставление неудобно ни для непосредственного вычисления значения 2. Путем динамической генерации программы на языке C#. функции, ни для генерации кода, вычисляющего ее значение. Поэтому 3. Путем динамической генерации метаданных и CIL-кода. нам понадобится парсер и некоторое представление, в которое этот парсер Затем мы сравним эффективность каждого способа. будет переводить введенную с клавиатуры текстовую строку. Детали синтаксического анализа и представления выражений мы 5.1.1. Обобщенный алгоритм интегрирования рассматривать не будем: интересующиеся могут обратиться к полным ис- Для интегрирования функций нам потребуется некое представление ходным текстам примера. Скажем лишь, что анализ осуществляется мето- функции, которое бы не зависело от конкретного способа вычисления дом рекурсивного спуска и транслирует выражение в дерево, в узлах кото- значения функции. Идеальным вариантом такого представления является рого расположены объекты, представляющие арифметические операции и абстрактный класс Function: их операнды. Каждый из этих объектов является экземпляром одного из public abstract class Function четырех классов, обозначающих числовые константы, переменные, унар- { ные и бинарные операции. Причем все эти классы наследуют от абстракт- public abstract double Eval(double x); ного класса Expression: } public abstract class Expression Объявив такой класс, мы предполагаем, что от него будут наследо- { ваться другие классы, реализующие в методе Eval конкретный способ вы- public abstract string GenerateCS(); числения значения функции в заданной точке. public abstract void GenerateCIL(ILGenerator il); Имея класс Function, мы можем записать обобщенный алгоритм ин- public abstract double Evaluate(double x); тегрирования методом прямоугольников. В качестве параметров этот ал- } горитм принимает объект f, представляющий интегрируемую функцию, В классе Expression объявлены три абстрактных метода, которые ка- пределы интегрирования a и b, а также количество разбиений n: ждый класс-наследник реализует по-своему. Метод Evaluate выполняет
Страницы
- « первая
- ‹ предыдущая
- …
- 87
- 88
- 89
- 90
- 91
- …
- следующая ›
- последняя »