Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > VB .NET > Как вычислить выражение записанное в виде строки


Автор: lvvas 1.4.2009, 15:47
Числовой переменной можно присвоить значение арифметического выражения и результат вывести в текстовую строку
например:

Dim a As Decimal = (2 + 2) * 3
TextBox1.Text = a

А как сделать так, чтобы строковые данные воспринимались как арифметическое выражение?
То есть, например, типа так:

TextBox1.Text = "(2 + 2) * 3"
Dim a As Decimal =TextBox1.Text
TextBox1.Text = a

(но не работает и не должно, поскольку переменной типа Decimal пытаюсь присвоить строковое выражение)

В общем, нужно чтобы вычислялось выражение, введённое в текстовую строку... 
Есть ли простые способы решения этой проблемы?





Автор: diadiavova 1.4.2009, 15:59
Цитата(lvvas @  1.4.2009,  15:47 Найти цитируемый пост)
Есть ли простые способы решения этой проблемы?

Можно использовать готовые библиотеки, коих вполне достаточно даже среди оупенсорс-проектов
Например:
http://flee.codeplex.com/
 или используй динамическую компиляцию. Например Так
http://www.interface.ru/home.asp?artId=4703.

Автор: -Mikle- 1.4.2009, 17:09
Цитата(diadiavova @  1.4.2009,  18:59 Найти цитируемый пост)
или используй динамическую компиляцию

Когда-то я таким способом делал. Имей ввиду, штука весьма противная, так как компиляция - процесс не быстрый уже по своему определению. Время выполнения составляло 100 мс, то есть 10 вычислений в секунду. Сложность выражения пркактически не влияла на скорость.

Вот код на C#, если будет не понятно, попроси, переведем на VB.NET
Код

    public static class Evaluator
    {
        static CodeDomProvider csharpProvider;
        static CompilerParameters compilerParameters;
        static string baseCodeBegin;
        static string baseCodeEnd;

        static Evaluator()
        {
            compilerParameters = new CompilerParameters();
            compilerParameters.GenerateInMemory = true;
            compilerParameters.ReferencedAssemblies.Add("mscorlib.dll");

            csharpProvider = CodeDomProvider.CreateProvider("c#");

            baseCodeBegin = "using System; public static class DynamicExpression { public static double Eval() { return \r\n";
            baseCodeEnd = "\r\n; } }";
        }

        public static double Evaluate(string expression)
        {
            CompilerResults result = csharpProvider.CompileAssemblyFromSource(compilerParameters, string.Concat(baseCodeBegin, expression, baseCodeEnd));
            if (result.Errors.HasErrors)
            {
                StringBuilder strB = new StringBuilder();
                foreach (CompilerError error in result.Errors)
                    strB.AppendLine(string.Format("[pos: {0}] - {1}", error.Column, error.ErrorText));
                throw new ArgumentException("Ошибка в выражении:\r\n" + strB.ToString(), "expression");
            }

            return (double)result.CompiledAssembly.GetType("DynamicExpression").GetMethod("Eval").Invoke(null, null);
        }
    }


Использование простое:
Код

            try
            {
                double res = Evaluator.Evaluate("2 + 2");
                MessageBox.Show(this, "Eval: " + res);
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }

Автор: diadiavova 1.4.2009, 17:23
Чтобы было быстро надо собственный парсер писать, а такое решение вряд ли можно назвать простым.
Кроме того: если надо производить много вычислений, то можно их все оформить в одну сборку и скомпилировать 1 раз. Хотя, в любом случае, я не агитирую за этот способ. smile 
ЗЫ
В моей второй ссылке пример на васике, сам не проверял, но, как мне кажется я эту статью видел несколько лет назад на другом сайте, если это она, то всё работает.

ЗЫ
Можно использовать генератор парсеров, но тоже к простым этот способ не относится, тк грамматику прийдётся описывать.

Автор: source777 1.4.2009, 18:07
Цитата(diadiavova @  1.4.2009,  17:23 Найти цитируемый пост)
Чтобы было быстро надо собственный парсер писать, а такое решение вряд ли можно назвать простым.
Зато можно назвать наиболее адекватным, т.к. строка может содержать функции, которых попросту нет в .NET, а если речь идёт исключительно о +-*/, то написать такой парсер будет самому даже быстрее, чем разобраться с CodeDomProvider.

Автор: diadiavova 1.4.2009, 18:12
Вопрос был о простом способе. А большинство операций, даже если их нет в Net, то можно добавить, описав свои функции. Другое дело, если речь идёт о каком-нибудь очень специфическом синтаксисе, тогда конечно, стандартные компиляторы тут не помогут.

Автор: source777 1.4.2009, 19:30
Цитата(diadiavova @  1.4.2009,  18:12 Найти цитируемый пост)
Вопрос был о простом способе. А большинство операций, даже если их нет в Net, то можно добавить, описав свои функции. 
В .NET, славбогу, нет глобальных функций, но заставлять пользователя твоего приложения писать System.Math.Sqrt вместо sqrt, System.Math.Power вместо ^ и т.д. и т.п, мягко говоря, негуманно.
 А простота к-л способа сильно зависит от того с какой целью понадобилось считать выражение в строке, зачастую динамическая компиляция далеко не самое простое решение данной задачи.

Автор: diadiavova 1.4.2009, 20:45
Объясню, как выполняется то, о чём я написал. 
Для выполнения операции текст фактически вставляется в тело кода сборки. То есть: для того, чтобы вычислить строку 2+2*2 надо написать что-то в этом роде
Код

using System;
namespace ns
{
     public class MyClass
      {
           int GetValue()
            {
                   return 2+2*2;
             }
       }
}


Такой код компилится, далее создаётся экземпляр класса MyClass и у него вызывается метод GetValue.
Если тебе надо добавить пару-тройку функций, то просто запихни их в этот класс и дело в шляпе.

Что до выражения 3^2 ,то это синтаксис бейсика и если использовать его компилятор и формировать код на нём же, то всё будет нормуль.

Автор: diadiavova 1.4.2009, 21:42
Выкладываю пример. Здесь можно выполнять операции в соответствии с синтаксисомвасика(включая степени), так же можно использовать все функции класса Math напрямую (то есть не Sysytem.Math.Sin, а просто Sin или sin), кроме того: я добавил свою функцию Factorial, которую тоже можно использовать в выражениях.

Автор: lvvas 2.4.2009, 07:28
ОГРОМНЕЙШЕЕ всем спасибо. Сижу, разбираюсь...

Автор: source777 2.4.2009, 15:20
Цитата(diadiavova @  1.4.2009,  21:42 Найти цитируемый пост)
то есть не Sysytem.Math.Sin, а просто Sin или sin
И бейсик всё это проглотит? ужос какой...

Цитата(diadiavova @  1.4.2009,  21:42 Найти цитируемый пост)
кроме того: я добавил свою функцию Factorial, которую тоже можно использовать в выражениях. 
Ну вот чему ты детей учишь? Если уж решил похвастаться, что понятие "рекурсия" тебе знакомо, то сделал бы её хотя бы хвостовой...


Автор: diadiavova 2.4.2009, 15:28
Цитата(source777 @  2.4.2009,  15:20 Найти цитируемый пост)
И бейсик всё это проглотит? ужос какой

Дело не в бейсике. Просто код посмотри, обрати внимание на инструкцию импорта(using по вашему smile ). А о том, что лучше регистрозависимые языки или нет можно, конечно поспорить, только это тема для другой категории.
Цитата(source777 @  2.4.2009,  15:20 Найти цитируемый пост)
Ну вот чему ты детей учишь? 

Не знал, что ты ребёнок...извини smile 
Цитата(source777 @  2.4.2009,  15:20 Найти цитируемый пост)
Если уж решил похвастаться, что понятие "рекурсия" тебе знакомо, то сделал бы её хотя бы хвостовой...

Какая рекурсия, ты вообще о чём? smile 

Автор: -Mikle- 2.4.2009, 15:46
Цитата(source777 @  2.4.2009,  18:20 Найти цитируемый пост)
И бейсик всё это проглотит? ужос какой...

и не только это  smile , еще и это:
Код

            Dim calc = res.CompiledAssembly.CreateInstance("Ns.ExprCalc")
            msg = calc.GetValue().ToString

Автор: source777 2.4.2009, 20:26
Цитата(diadiavova @  2.4.2009,  15:28 Найти цитируемый пост)
Дело не в бейсике. Просто код посмотри

Я посмотрел, потому и говорю, что ужос полный. И тут дело именно в бейсике!

Цитата(diadiavova @  2.4.2009,  15:28 Найти цитируемый пост)
Не знал, что ты ребёнок...извини smile 
Я не про себя, а про lvvas и ему подобных, они ведь могут твой код воспринять всерьёз и не понять злобной шутки:
Код

    Function Factorial(ByVal n As UInteger) As Integer
        If n = 0 OrElse n = 1 Then
            Return 1
        End If
        Return n * Factorial(n - 1)
    End Function





Автор: diadiavova 2.4.2009, 21:48
Цитата(source777 @  2.4.2009,  20:26 Найти цитируемый пост)
Я не про себя, а про lvvas и ему подобных, они ведь могут твой код воспринять всерьёз и не понять злобной шутки:

Эта "злобная шутка" была всего лишь иллюстрацией вот этого
Цитата(diadiavova @  1.4.2009,  18:12 Найти цитируемый пост)
А большинство операций, даже если их нет в Net, то можно добавить, описав свои функции.

и адресована была лично тебе. И честно говоря мне и в голову не пришло, что кто-то может меня заподозрить в том, что я пытаюсь кого бы то ни было учить вычислять факториал(речь, как бы не нём была). 

Автор: source777 2.4.2009, 22:16
Цитата(diadiavova @  2.4.2009,  21:48 Найти цитируемый пост)
Эта "злобная шутка" была всего лишь иллюстрацией вот этого
Такой метод может быть иллюстрацией только к тому как не надо ни в коем случае писать код... 
Но в целом тебе конечно удалось показать как расширить границы применимости данного метода за счёт нестрогого синтаксиса бейсика. 

Автор: diadiavova 2.4.2009, 23:25
Цитата(source777 @  2.4.2009,  22:16 Найти цитируемый пост)
Такой метод может быть иллюстрацией только к тому как не надо ни в коем случае писать код...

А как надо? Просто даже интересно, какую альтернативу ты предлагаешь. Писать свой парсер?

Цитата(source777 @  2.4.2009,  22:16 Найти цитируемый пост)
Но в целом тебе конечно удалось показать как расширить границы применимости данного метода за счёт нестрогого синтаксиса бейсика. 

Строгость в бейсике - штука, вполне поддающаяся настройке. Просто он более гибок.

Автор: -Mikle- 3.4.2009, 09:45
source777 и diadiavova, если будете продолжать, я отрежу кусочек темы и отправлю его вместе с вами в религиозные войны  smile  Устроить это???

Автор: source777 3.4.2009, 11:09
Цитата(diadiavova @  2.4.2009,  23:25 Найти цитируемый пост)
Писать свой парсер?
Для вычисления факториала свой парсер писать не нужно, но твой метод вычисления факториала достоин помещения в кунсткамеру.

2-Mikle-
Можно просто закрыть тему  smile 

Автор: -Mikle- 3.4.2009, 11:39
Цитата(source777 @  3.4.2009,  14:09 Найти цитируемый пост)
Можно просто закрыть тему

А здравый смысл вас не остановит?

Ок. Я подожду пока вы выскажетесь тут друг другу, а потом отправлю все это в мусорку... Закрывать не буду, так как тема может быть полезной...

Автор: diadiavova 3.4.2009, 12:29
source777, Я не понимаю: мы что обсуждали факториалы? Ты название темы перечитай. И то, что я написал рассматривай в её контексте.

Немного доработал свой пример, а то у людей действительно может сложиться впечатление, что всё получилось только из-за "нестрогого синтаксиса бейсика". В примере можно выбрать язык. Причём для шарпа два режима: в первом функции надо писать как они определены в классе System.Math (то есть с  большой буквы), а во втором надо использовать нижний регистр. Кода получилось больше чем для бейсика, да и на совершенство он тоже не претендует, а только демонстрирует возможности динамической компиляции(при чём далеко не все).

 

Автор: m08pvv 20.4.2009, 15:33
Недавно в универе задали такое задание...
Сразу же вспомнил старую програмку, которую пришлось писать заново, глядя на картинку с хитрой структурой, а затем дебажить и добавлять функционал
Итак, представляю всем желающим свое творение, которое всего 666 строк (с комментариями на английском в минимальном количестве)

Автор: m08pvv 23.4.2009, 16:13
Заметил пару багов (связанных со сравнениями)
Вот новая версия


Подправил унарный минус (случайно наткнулся на то, что в порыве его туда добавить испортил простой минус...)

вот новая версия (если кому надо)

Автор: vladd 1.9.2015, 10:35
Привет.

Имею код 
Код

Dim n As Integer = 0

Label1.Invoke(Sub() n = Val(Label1.Text),   n = n + 1,   Label1.Text = Str(n))


Такая ошибка
Код

Conversion from string "Label1" to type 'Integer' is not valid.


 Как сделать правильно? В чём ошибка?

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)