Динамический анализ / вычисление логических выражений в C # или VB?

StackOverflow https://stackoverflow.com/questions/350181

  •  20-08-2019
  •  | 
  •  

Вопрос

Лучше всего было бы вычислить выражение, подобное следующему:
(A И B) Или (A И C) Или (Не B И C)
или
(A && B) || (A && C) || (!B && C)

Во время выполнения я планировал преобразовать приведенные выше выражения в следующее:
(Истина и ложь) Или (Истина и ложь) Или (Не Ложь и не истина)
или
(Истина && False) || (Истина && False) || (!Ложь и& Истина)

Условия:1) Логическое выражение неизвестно до времени выполнения.2) Переменная number и их значения неизвестны до времени выполнения.3) Значения переменных никогда не равны нулю.

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

Предложения?

Спасибо.

Это было полезно?

Решение

Если вы используете .NET3.5, то вы можете проанализировать текст и создать абстрактное дерево sytax, используя классы Expression .Затем создайте подходящий экземпляр LambdaExpression и скомпилируйте его в делегат, который затем вы можете выполнить.

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

Если вы не используете .NET3.5, то также несложно самостоятельно реализовать интерпретируемое абстрактное синтаксическое дерево.

Другие советы

Будьте предупреждены:два последних условия, о которых вы говорите, не обязательно эквивалентны.Операторы && в C # будут использовать вычисление короткого замыкания, в то время как логический And оператор в VB этого не делает.Если вы хотите быть уверены, что инструкции эквивалентны, переведите пользователя And Для AndAlso и пользователь Or Для OrElse.

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

Вы можете легко сделать это с помощью:

  1. генератор синтаксического анализа (подобный ANTLR, упомянутый выше), который принимает логические выражения в качестве входных данных и создает список инфиксов и
  2. код для вычисления стека обратных польских обозначений.

Грамматика выглядит примерно так:

program: exprList ;

exprList: expr { Append($1); }
    | expr OR exprList { Append(OR); }
    | expr AND exprList { Append(AND); }
    | NOT exprList { Append(NOT); }
    | ( exprList ) { /* Do nothing */ }
    ;

expr: var { Append($1); }
    | TRUE { Append(True); }
    | FALSE { Append(False); }
    ;

Чтобы оценить, вы делаете это:

for each item in list
    if item is symbol or truth value, push onto RPN stack
    else if item is AND, push (pop() AND pop())
    else if item is OR, push (pop() OR pop())
    else if item is NOT, push (NOT pop())

result = pop()

Для символов вы должны заменить значение истинности во время выполнения.

Вы можете использовать https://github.com/mrazekv/logicalparser

Это просто библиотека для записи логического выражения (вычисляется с помощью предварительной таблицы, допускает оператор OR, NOT, AND и >, >=, <=, < для целочисленных переменных и = для строковых переменных)

Вы можете написать простой интерпретатор / синтаксический анализатор.Используйте что-то вроде ANTLR ( АНТЛР ) и повторно используйте существующие грамматики.

Если вы используете .NET 3.5, вы можете создать лямбда-выражение.Затем вы можете создать делегат из него и вызвать как стандартный делегат / метод.В интернете есть много примеров о лямбда-выражениях.

Одним из решений было бы собрать выражение в виде строки, а затем отправить его SQL Server или любой другой вашей базе данных для оценки.Замените фактические переменные на 1= 1 или 0 = 1 для True и False соответственно, и в итоге вы получите запрос, подобный этому:

ВЫБЕРИТЕ 1, ГДЕ (1=1 И 0=1) Или (1=1 И 1=1) Или (Не 0=1 и 1=1)

Затем, когда вы запускаете запрос, вы получаете обратно 1, когда результат равен true.Возможно, это не самое элегантное решение, но оно сработает.Многие люди, вероятно, будут отговаривать от этого, но я все равно просто собираюсь предложить это как возможное решение.

Это будет не самый лучший ответ, но у меня самого была эта проблема некоторое время назад.

Вот мой старый код:VB.Net - вообще никакой гарантии!

https://cloud .downfight.de/index.php/s/w92i9Qq1Ia216XB

Dim BoolTermParseObjekt As New BoolTermParse
MsgBox(BoolTermParseObjekt.parseTerm("1 und (((0 oder 1 und (0 oder 4))) oder 2)").ToString)

Этот код использует строку с несколькими '(', ')', 'и', 'или' плюс 'другие вещи' и разбивает логику на логическое значение, заменяя вещи логическими значениями.следовательно:

Какие бы "другие вещи" я ни хотел оценить, я должен был ввести в функцию resolveTerm() at the comment "'funktionen ausführen und zurückgeben, einzelwert!" на странице 2.Там единственная оценка прямо сейчас - "Если число > 1".

Приветствия

Взгляните на мою библиотеку, Proviant.Это стандартная библиотека .NET, использующая Алгоритм маневровой площадки для вычисления логических выражений.

Это также может сгенерировать таблицу истинности для ваших выражений.

Вы также могли бы реализовать свою собственную грамматику.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top