Интерпретация строковых выражений как функций
Автор: Константин Александров Иногда возникает потребность интерпретировать в программе строковое выражение как функцию. Например, при написании графопостроителя требуется переработать полученную строку так, чтобы в результате получилась функция, причем работающая со скоростью, необходимой для многократного пересчета координат точек. На самом деле круг подобных задач намного шире, он включает в себя программы, использующие любые варианты условий отбора (например, интерпретация условий SQL запросов). Эта статья посвящена решению задач такого рода, правда я не буду приводить готовых исходников, которые можно скачать и скомпилировать, моя задача - показать одну из возможностей реализации. Кому это нужно и интересно, сами напишут все, что надо и разовьют идею.
Примеры в статье будут написаны на PERL только потому, что этот язык является достаточно гибким и не придется отвлекать внимание от задачи на особенности реализации под конкретный язык.
$A == 34 AND $C != 5 После выделения функции заменяем ее в исходной строке возвращаемым значением:
[Значение1] AND $C != 5, Последнее значение и будет являться значением выражения, которое требуется найти (Далее будет рассматриваться работа только с простыми выражениями). Однако, если использовать переработку исходной строки с заменой функции на её значение при конкретных значениях переменных, то будут серьёзные потери производительности. Поэтому логичным является использование не самих значений, а указателей на них. Для этого потребуется хранить значения самих переменных, констант и значений функций.
# Массив аргументов - констант Для тех, кто не знаком с синтаксисом PERL поясню, что если перед именем переменной стоит знак $, то переменная является скаляром и может содержать любое единичное значение: строку, число, указатель на объект. Если @, то это массив скаляров, обращаться к каждому элементу массива можно используя имя массива с указанием перед ним знака $ (то есть элемент массива - скаляр) и индекса в квадратных скобках после имени (например, $m[0] - скаляр, первый элемент массива, а @m - массив). Если %, то это ассоциативный массив, обращаются к нему так же, как и к обычному, только вместо индекса в квадратных скобках указывается строковое выражение в фигурных (например, $CountDays{'Май'} = 31 - элемент ассоциативного массива, имеющий скалярное значение 31; при этом строку 'Май' можно сопоставить с индексом обычного массива; %CountDays - ассоциативный массив). Следует отметить, что строка разбирается только один раз при заполнении массивов, поэтому последующие расчеты выполняются быстрее.
# Определение функции "==" Теперь несколько слов о вызове функции и о задании её аргументов. С константным аргументом всё просто, его значение сохранено в массиве констант и при каждом расчете значения функции оно будет использоваться. Значение же переменной $A будет храниться в элементе ассоциативного массива $zn_p{'$A'}. Его можно будет легко задавать перед каждым расчетом.
# Запоминаем текущее значение переменной Знак $$ перед именем переменных F_arg_p1 и F_arg_p2 означает, что при расчете нужно брать не значения этих переменных (в них лежат указатели), а данные, на которые они указывают. Знак &$ перед именем F_Name_p означает, что нужно вызвать функцию, указатель на которую записан в переменной $F_Name_p Все, что было описано в статье, можно использовать и для интерпретации сложных выражений, просто в таком случае они будут представляться не одной функцией, а набором функций (указатели на них логично записать в массив и вызывать их последовательно). Наверняка найдутся читатели, которые скажут, что реализация такой задачи на PERL не нужна, или её можно реализовать с использованием стандартных средств языка. Они конечно будут правы, однако подход, примененный для решения задачи позволяет сделать аналог на другом языке программирования, например на СИ (язык поддерживает все методы, которые использовались в данном примере) |