Как я могу создать генератор таблиц истинности?

Я собираюсь написать генератор таблиц истинности в качестве личного проекта.

Есть несколько веб-сайтов в Интернете здесь и здесь.

alt text
(Example screenshot of an existing Truth Table Generator)

У меня есть следующие вопросы:

  • Как мне разобрать такие выражения, как: ((P => Q) & (Q => R)) => (P => R)
  • Должен ли я использовать генератор синтаксических анализаторов, такой как ANTLr или YACC, или использовать прямые регулярные выражения?
  • Как только я проанализирую выражение, как мне создать таблицу истинности? Каждый раздел выражения необходимо разделить на самые мелкие компоненты и перестроить с левой стороны таблицы на правую. Как бы я оценил нечто подобное?

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


person KingNestor    schedule 05.07.2009    source источник
comment
Регулярные выражения не будут работать из-за произвольного количества скобок. Вам нужно будет использовать какой-то парсер-генератор.   -  person Samir Talwar    schedule 06.07.2009
comment
Думаю, эти исходники (mrieppel.net/prog/truthtable.html) полезны.   -  person yv4    schedule 25.04.2015


Ответы (5)


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

То, как работают такие системы, — это формализация того, как мы понимаем естественные языки. Если я даю вам предложение: «Собака, Ровер, съел свою еду», первое, что вы делаете, это разбиваете его на слова и ставите знаки препинания. «The», «SPACE», «собака», «COMMA», «SPACE», «Rover», ... Это «токенизация» или «лексинг».

Следующее, что вы делаете, это анализируете поток токенов, чтобы увидеть, является ли предложение грамматическим. Грамматика английского языка чрезвычайно сложна, но это предложение довольно простое. ПОДЛЕЖАЩЕЕ-ПРИЛОЖИТЕЛЬНОЕ-ГЛАГОЛ-ОБЪЕКТ. Это "разбор".

Как только вы узнаете, что предложение является грамматическим, вы можете проанализировать предложение, чтобы на самом деле извлечь из него смысл. Например, вы можете видеть, что в этом предложении есть три части — подлежащее, прилагательное и «его» в дополнении — и все они относятся к одному и тому же объекту, а именно к собаке. Вы можете понять, что собака — это то, что ест, а еда — это то, что едят. Это этап семантического анализа.

Затем у компиляторов есть четвертая фаза, которой нет у людей: они генерируют код, представляющий действия, описанные в языке.

Итак, сделайте все это. Начните с определения токенов вашего языка, определите токен базового класса и набор производных классов для каждого. (IdentifierToken, OrToken, AndToken, ImpliesToken, RightParenToken...). Затем напишите метод, который принимает строку и возвращает IEnumerable. Это твой лексер.

Во-вторых, выясните, что такое грамматика вашего языка, и напишите синтаксический анализатор рекурсивного спуска, который разбивает IEnumerable на абстрактное синтаксическое дерево, представляющее грамматические сущности вашего языка.

Затем напишите анализатор, который просматривает это дерево и вычисляет такие вещи, как «сколько различных свободных переменных у меня есть?»

Затем напишите генератор кода, который выдает код, необходимый для оценки таблиц истинности. Плюнуть IL кажется излишним, но если вы хотите быть действительно крутым, вы можете это сделать. Может быть проще позволить библиотеке дерева выражений сделать это за вас; вы можете преобразовать дерево синтаксического анализа в дерево выражений, а затем превратить дерево выражений в делегат и оценить делегат.

Удачи!

person Eric Lippert    schedule 05.07.2009
comment
Вы упомянули об использовании IEnumerable для представления потока токенов. Что бы вы предложили использовать для представления AST? - person KingNestor; 06.07.2009
comment
Программа представляет собой последовательность токенов, но только ОДНО абстрактное синтаксическое дерево. Обычно вы определяете программный узел, который может содержать любую возможную программу, но в вашем случае грамматика будет очень простой; это, вероятно, будут просто двоичные выражения. У меня был бы просто базовый класс Expression, а затем набор производных классов, OrExpression, ImpliesExpression, IdentifierExpression и так далее. Выражение OrExpression имеет двух дочерних элементов, которые сами являются выражениями. И так далее. - person Eric Lippert; 06.07.2009
comment
Так что это компилятор менее чем в 1000 слов ... блестящая штука - person flesh; 17.07.2009
comment
Эрик, похоже, самым сложным шагом выше является семантический анализ (если я правильно понял, перевод IEnumable в дерево со значением). Как вы предлагаете это сделать? Я понимаю концепцию AST, но как узнать, с чего начать в IEnumerable? - person flesh; 17.07.2009

Я думаю, что генератор парсеров - это излишество. Вы можете использовать идею преобразования выражения в постфикс и вычисления постфиксных выражений (или напрямую построение дерева выражения из инфиксного выражения и использование его для создания таблицы истинности) для решения этой проблемы.

person mmx    schedule 05.07.2009
comment
Но это создание синтаксического анализатора, будь то ручная работа. Если вы знаете, как использовать Lex (или что-то подобное), вы также знаете, как его свернуть вручную. - person Simeon Pilgrim; 06.07.2009
comment
Это это синтаксический анализатор, но его могут использовать студенты компьютерных наук в первом семестре для вычисления арифметических выражений. Я сомневаюсь, что весь программный движок будет состоять из более чем 100 строк кода (включая оценку и генерацию таблицы истинности). - person mmx; 06.07.2009
comment
Я согласен, первое, о чем я подумал, было о моем первом курсе Postfix в колледже. Этот проект будет очень похож на тот в целом. - person Neil N; 15.05.2010

Как упоминает Мехрдад, вы должны быть в состоянии выполнить синтаксический анализ вручную за то же время, что и для изучения синтаксиса лексера/парсера. Конечным результатом, который вам нужен, является некоторое абстрактное синтаксическое дерево (AST) выражения, которое вы получили.

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

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

Как бы я это сделал:

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

person Simeon Pilgrim    schedule 05.07.2009

Если вашей целью является обработка логических выражений, генератор синтаксического анализатора и все сопутствующие механизмы — пустая трата времени, если только вы не хотите узнать, как они работают (тогда подойдет любой из них).

Но легко вручную создать анализатор рекурсивного спуска для логических выражений, который вычисляет и возвращает результаты «вычисления» выражения. Такой синтаксический анализатор можно использовать при первом проходе для определения количества уникальных переменных, где «оценка» означает «счет 1 для каждого нового имени переменной». Написание генератора для получения всех возможных значений истинности для N переменных тривиально; для каждого набора значений просто снова вызовите синтаксический анализатор и используйте его для оценки выражения, где оценка означает «объединить значения подвыражений в соответствии с оператором».

Вам нужна грамматика:

formula = disjunction ;
disjunction = conjunction 
              | disjunction "or" conjunction ;
conjunction = term 
              | conjunction "and" term ;
term = variable 
       | "not" term 
       |  "(" formula ")" ;

У вас может быть сложнее, но для логических выражений это не может быть намного сложнее.

Для каждого правила грамматики напишите 1 подпрограмму, которая использует глобальный индекс «сканирования» в анализируемой строке:

  int disjunction()
 // returns "-1"==> "not a disjunction"
 // in mode 1:
 // returns "0" if disjunction is false
 // return  "1" if disjunction is true
 { skipblanks(); // advance scan past blanks (duh)
   temp1=conjunction();
   if (temp1==-1) return -1; // syntax error
   while (true)
     { skipblanks();
       if (matchinput("or")==false) return temp1;
       temp2= conjunction();
       if (temp2==-1) return temp1;
       temp1=temp1 or temp2;
     }
  end

  int term()
  { skipblanks();
    if (inputmatchesvariablename())
       { variablename = getvariablenamefrominput();
         if unique(variablename) then += numberofvariables;
         return lookupvariablename(variablename); // get truthtable value for name
       }
     ...
  }

Каждая из ваших подпрограмм синтаксического анализа будет примерно такой сложной. Шутки в сторону.

person Ira Baxter    schedule 22.08.2009

Вы можете получить исходный код программы pyttgen по адресу http://code.google.com/p/pyttgen/source/browse/#hg/src Создает таблицы истинности для логических выражений. Код основан на библиотеке слоев, так что это очень просто :)

person RANUX    schedule 14.05.2010