The website "dmilvdv.narod.ru." is not registered with uCoz.
If you are absolutely sure your website must be here,
please contact our Support Team.
If you were searching for something on the Internet and ended up here, try again:

About uCoz web-service

Community

Legal information

5.8 Математические функции — Math

5.8 Математические функции — Math

Предыдущая  Содержание  Следующая V*D*V

ANSI-C определяет ряд математических функций, такие как sin(), sqrt(), exp() и так далее. В качестве другого упражнения в наследовании, мы собираемся добавить в наш калькулятор библиотечные функции с одним параметром типа double и результатом типа double.

Эти функции работают в значительной степени подобно унарным операторам. Можно было бы определить новый тип узла для каждой функции и собрать большую часть функциональности из классов Minus и Name, но есть более простой способ. Мы расширим struct Name в struct Math следующим образом:

 

struct Math { struct Name _;

    double (* funct) (double);

};

 

#define funct(tree) (((struct Math *) left(tree)) -> funct)

 

В дополнение к имени функции, которое будет использоваться при вводе, и маркера для распознавания сохраняем адрес библиотечной функции, подобной sin(), в записи таблицы символов.

Для добавления всех описаний функций в таблицу символов во время инициализации вызываем следующую функцию:

 

#include <math.h>

 

void initMath (void) {

    static const struct Math functions [] = {

        { &_Math, "sqrt", MATH, sqrt },

        ...

        0 };

 

    const struct Math * mp;

 

    for (mp = functions; mp -> _.name; ++ mp)

        install(mp);

}

 

Вызов функции является коэффициентом, так же как и использование знака минус. Для присвоения коэффициентов надо расширить нашу грамматику:

 

factor : NUMBER

    | - factor

    | ...

    | MATH ( sum )

 

MATH является общим маркером для всех функций, добавляемых initMath(). Это превращается в следующее дополнение к factor() в распознавателе:

 

static void * factor (void) {

    void * result;

    ...

    switch (token) {

    case MATH: {

        const struct Name * fp = symbol;

 

        if (scan(0) != ’(’)

            error("expecting (");

        scan(0);

        result = new(Math, fp, sum());

        if (token != ’)’)

            error("expecting )");

        break;

    }

 

symbol сначала содержит элемент таблицы символов для функции подобной sin(). Мы сохраняем этот указатель и строим дерево выражений для аргумента функции вызовом sum(). Затем используем Math, описание типа для функции, и предоставляем new() построить следующий узел для дерева выражений:

 

OOC_Mathematical_Functions-Math

 

Левой стороне бинарного узла предоставляем указывать на элемент таблицы символов для этой функции и подключаем дерево аргументов справа. Бинарный узел имеет Math в качестве описания типа, то есть для обработки и удаления узла будут вызываться методы doMath() и freeMath() соответственно.

Узел Math по-прежнему конструируется с помощью mkBin(), так как эту функцию не заботит, какие указатели введены в качестве потомков. freeMath(), тем не менее, может удалить только поддерево правой стороны:

 

static void freeMath (void * tree)

{

    delete(right(tree));

    free(tree);

}

 

Если внимательно посмотреть на картинку, можно увидеть, что работа узла Math очень проста. doMath() требуется вызвать какую-то функцию, хранящуюся в таблице символов элемента, доступного в виде левого потомка бинарного узла, от которого она вызывается:

 

#include <errno.h>

 

static double doMath (const void * tree) {

    double result = exec(right(tree));

 

    errno = 0;

    result = funct(tree)(result);

    if (errno)

        error("error in %s: %s",

            ((struct Math *) left(tree)) -> _.name,

            strerror(errno));

    return result;

}

 

Единственная проблема заключается отлове числовых ошибок мониторингом переменной errno, задекларированной в заголовочном файле errno.h ANSI-C. Это завершает реализацию математических функций калькулятора.

 

Предыдущая  Содержание  Следующая