Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Последние темы форума

Показать новые сообщения »

Почтовая рассылка

Подписчиков: 11642
Последний выпуск: 19.06.2015

Интерпретатор математических формул на VB

Автор: Илья Агеев
12 августа 2004 года

Преамбула

Как-то взялся я писать программу. Ничего особенного, программа, как программа, база данных, интерфейс, стандартный учет стандартной информации. Да вот только было в ней и кое-что интересное. Некоторые поля должны были рассчитываться на основе других, причем заранее не известно, на каких именно и каким образом. Т.е. надо было писать редактор формул, в котором можно было бы указывать какие поля взять и что с ними делать. Соответственно, потом по этим формулам надо было рассчитывать новое значение и заносить в таблицу.

С первой частью справиться труда не составило, и часов через несколько редактор был готов. Пользователь брал поля, которые были основой для расчета, компоновал из них формулу, разделяя все скобками и знаками арифметических действий. Короче, все кто видел построитель выражений в Microsoft программах, поймет, что у меня получилось в итоге.

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

Полезным оказался только один пример на Дельфи, в котором данная задача решалась, но во-первых, все равно надо было переписывать на VB, а во-вторых, там было все на столько сложно сделано, что удивительно, как это вообще работает.

По этому, было принято решение писать самостоятельно. И вот что из этого получилось.

Амбула

Итак, что сбой представляет любая формула, с математическими функциями, возведениями в степень и скобками? Это последовательность действий, которые необходимо осуществить с элементами формулы, чтобы получить в итоге результат. Все действия имеют разный приоритет. Нам необходимо научить компьютер осуществлять эти действия.

Любая формула, сколько бы в ней элементов и операций не было, всегда упрощается до простой арифметики с четырьмя типами математических операций: +,-,/,*.

Например:

50+sin(1)*cos(0)/(450/(78+45)) рассчитывая последовательно выражения внутри скобок и функции получим:

	sin(1)=0
	cos(0)=1
	(78+45)=123
	450/123=3.65

50+0*1/3.65 - вот функция, упрощенная до простых арифметических операций.

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

Нам необходимо представить текстовую строку, которая не имеет никакой смысловой нагрузки для VB в таком виде, чтобы мы могли работать с элементами формулы как с отдельными самостоятельными элементами, т.е. цифрами и знаками арифметических выражений. Причем должна сохраняться связь между ними и порядок действий. Первый выход, который показался мне реальным - массив. Итак,

Public Const opPlus As String = "+"
Public Const opMinus As String = "-"
Public Const opDivide As String = "/"
Public Const opMult As String = "*"

'Объявим константы - арифметические действия.

'Интерпретацией будет заниматься функция,
'аргумент которой есть текстовая строка-функция
Public Function Interpritate(strLine As String) As Double
On Error GoTo err_:
Dim tmp As String, mdr As String, stack() As String
'Текстовый динамический массив - это формула,
' с которой будет работать интерпретатор.
Dim i As Integer, st As Integer, t As Integer, i1 As Integer, t1 As Integer
st = 1 'Кол-во элементов формулы
For i = 1 To Len(strLine)
    mdr = Mid$(strLine, i, 1)
    If mdr = opPlus Or mdr = opMinus Or mdr = opDivide Or mdr = opMult Then
	'Если в формуле есть знак арифметического действия, то
            st = st + 1                 'занесем
            ReDim Preserve stack(st)    'предыдущий элемент и
            stack(st) = mdr1             'знак в стек
            stack(st - 1) = Trim(tmp)
            tmp = ""
            st = st + 1
    Else
	'Продолжаем считывать число
        tmp = tmp & mdr
    End If
Next i
'Занесем последний элемент в стек
ReDim Preserve stack(st)
stack(st) = tmp

'Все, массив готов. Можно интерпретировать ;-)

For i1 = 1 To 4
    Select Case i1 'Порядок арифметических действий
    Case 1
        tmp1 = opMult
    Case 2
        tmp = opDivide
    Case 3
        tmp = opPlus
    Case 4
        tmp = opMinus
    End Select
    i = 1
    While i <= st - 1
        If stack(i) = tmp Then
            Select Case tmp
            Case opMult
                stack(i - 1) = CDbl(stack(i - 1)) * _
                                CDbl(stack(i + 1))
            Case opDivide
                stack(i - 1) = CDbl(stack(i - 1)) / _
                                CDbl(stack(i + 1))
            Case opPlus
                stack(i - 1) = CDbl(stack(i - 1)) + _
                                CDbl(stack(i + 1))
            Case opMinus
                stack(i - 1) = CDbl(stack(i - 1)) - _
                                CDbl(stack(i + 1))
            End Select
	    'Запомним рассчитанное значение, а ячейки, на основе которых велся
	    'расчет, обнулим
            stack(i) = ""
            stack(i + 1) = ""
	    'Сдвинем остальные элементы к началу массива
            For t = i To st - 2
                stack(t) = stack(t + 2)
            Next t
            stack(st - 1) = ""
            stack(st) = ""
            i = i - 1
        End If
        i = i + 1
    Wend
Next i1
'В результате сдвига рассчитанных значений в начало массива, результат выражения
'находится в первой ячейке массива.
Interpritate = CDbl(stack(1))
Exit Function

err_:
    MsgBox "Ошибка #" & err.Number & vbCrLf & err.Description, vbCritical, "Внимание!"
    Interpritate = 0
End Function

P.S.: Движок готов. Все реально работает, но:

Во-первых: Статья была написана только в образовательных целях, поэтому просто скопировать текст и вставить в модуль не получится. Я намеренно изменил кое-что в коде, но если голова на месте, поняв принцип работы интерпретатора, исправите сами и все заработает.

Во-вторых: Это только движок, и формулы со скобками и функциями он не считает. Чтобы работать со скобками, необходимо последовательно интерпретировать выражения внутри скобок, заменять их в формуле полученным результатом, и, в итоге, интерпретировать упрощенное выражение. С функциями то же самое.

В-третьих: Подумайте сами, как это все заставить работать с отрицательными числами.

P.P.S.: У меня все это работает (включая скобки и т.д.). Будут вопросы - пишите.

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог