Какие модели вычислений можно выразить через грамматику?

18

Это переформулировка программ грамматики? предыдущий вопрос от Vag и множество предложений от комментаторов.

Каким образом грамматика может рассматриваться как спецификация модели вычислений? Если, например, мы берем простую контекстно-свободную грамматику, такую ​​как

G ::= '1' -> '0' '+' '1'
      '1' -> '1' '+' '0'
      '2' -> '2' '+' '0'
      '2' -> '1' '+' '1'
      '2' -> '0' '+' '2'
      '3' -> '3' '+' '0'
      '3' -> '2' '+' '1'
      '3' -> '1' '+' '2'
      '3' -> '1' '+' '2'

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

Например, взять строку

"2 + 0 + 1"

Запуск синтаксического анализатора LR (1) для этой строки должен дать нам следующее конкретное синтаксическое дерево, в котором результат вычисления сохраняется в корне дерева:

           '3'
         /  |  \
        /   |   \
      '2'  '+'  '1'
     / | \
    /  |  \
  '2' '+' '0' 

Таким образом, если мы возьмем грамматику в качестве программы и генератор синтаксического анализатора в качестве компилятора , можем ли мы рассматривать язык спецификации грамматики в качестве языка программирования ?

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

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

Кроме того, как насчет менее известных подклассов грамматик, таких как

РЕДАКТИРОВАТЬ: Между прочим, это мой клеветник по моему собственному вопросу, но я не упомянул, что я не дал начальный символ для примера грамматики и помахал рукой при необходимости различать терминалы и нетерминалы. Технически или традиционно я думаю, что грамматика, вероятно, должна была бы быть написана в более сложной форме, такой как эта (где S - начальный символ, а $ - терминал конца потока):

G ::= S -> R0 '$'
      S -> R1 '$'
      S -> R2 '$'
      R0 -> '0'
      R0 -> R0 '+' '0'
      R1 -> '1'
      R1 -> R0 '+' '1'
      R1 -> '1' '+' R0
      R1 -> R0 '+' '1' '+' R0
      R2 -> '2'
      R2 -> R0 '+' '2'
      R2 -> '2' '+' R0
      R2 -> R0 '+' '2' '+' R0
      R2 -> R1 '+' '1'
      R2 -> R1 '+' '1' '+' R0

... не то чтобы это действительно что-то меняет, но я подумал, что должен это упомянуть.

РЕДАКТИРОВАТЬ: Что-то еще, что пришло в голову, когда я прочитал ответ Гаше, это то, что каждая ветвь в дереве в моем примере представляет подсчет. Если вы посмотрите на каждое производственное правило как на функцию, в которой LHS представляет результат, а RHS представляет его аргументы, то структура грамматики определяет, как составляются функции.

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

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

Рено Линдек
источник
3
Вы забыли упомянуть о Visible Pushdown Automaton (Nested Words), таком прекрасном и многообещающем устройстве! Это важно, потому что, по-видимому, минимальное улучшение по сравнению с регулярными выражениями позволяет анализировать программы, написанные на популярных языках программирования. ( Cis.upenn.edu/~alur/nw.html )
Vag
1
Спасибо, это очень интересно, я не смотрел это! Есть пара других, которые я также пропустил, как детерминированный контекстно-свободный, примыкающий к дереву, проиндексированный и т. Д., Я просто думал, что это может быть немного много для одного вопроса ... Но, возможно, я добавлю их
Рено Линдек
1
@imz Я имею в виду грамматики, поскольку они формально определены в иерархии Хомского (т. е. как наборы произведений). Поскольку я утверждаю именно то, что вы говорите: грамматики - это программы, это просто означает класс программ, представляемых грамматиками (вот в чем вопрос).
Рено Линдек
1
@imz Если честно, я на самом деле не знаком с индексированными грамматиками, я добавил их только в качестве запоздалой мысли.
Рено Линдек
1
Я начинаю думать, что, возможно, было бы хорошей идеей опубликовать этот вопрос на форуме LtU, вместо этого, глядя на классные обсуждения: P. Кстати, @imz, возможно, было бы лучше прочитать вопрос как «каким классам грамматик соответствуют какие классы программ в« функциональном »смысле, описанные Юккой в ​​ответе Марка Хаммана». Возможно, я должен сделать это более ясным, хотя ...
Рено Линдек

Ответы:

10

Между грамматиками Хомского типа 0 и машинами Тьюринга существует однозначное соответствие .

Это используется в языке программирования Thue, который позволяет вам писать программы, полные по Тьюрингу, заданные исходной строкой и набором правил перезаписи строк ( грамматика полу-Thue , которая эквивалентна грамматике типа 0).

ОБНОВИТЬ:

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

Языки в семействе Lisp , в частности Common Lisp , являются, пожалуй, наиболее очевидными примерами, но в целом это также языки со статической проверкой типов, которые не всегда должны останавливаться, такие как C ++ с шаблонами , Scala и Qi .

Антонио Валерио Мицели-Бароне
источник
Но вопрос о вещах, которые работают наоборот: к результату нужно прийти не переписывая начальную последовательность символов в соответствии с правилами, а «результат» вычисления, определенный грамматикой в ​​этом вопросе, является начальным символ, который может производить последовательность «ввода» в соответствии с правилами грамматики.
imz - Иван Захарьящев
2
соNсaT(QUоTе(яN),оUT)TM(яN)знак равнооUT
Я согласен с тем, что соответствие между грамматиками Type0 и ТМ является верным ответом на этот вопрос (особенно, если он ограничен вычислением функций да / нет). Мне кажется, что дальнейшее предложение смоделировать произвольную ТМ с помощью грамматики путем введения некоторого соглашения о том, как представлять пары ввода-вывода, не соответствует предполагаемому интересу исходного вопроса: (продолжение)
imz - Иван Захарящев
Я понимаю это как вопрос для использования именно существующих грамматических структур и соответствующих синтаксических анализаторов для выполнения вычислений, т. Е. Разрешенная форма перевода между функцией f и грамматикой может быть только: входной сигнал, который я анализировал как S, означает f ( I) = S.
imz - Иван Захарящев
1
Внешне язык программирования Thue, похоже, не подпадает под этот вид использования грамматического фреймворка: хотя он и переписывает правила, такие как грамматика, вычисление результата от ввода идет в направлении правил, а не наоборот направление, как хочет Рено. (Но, возможно, это только вопрос изменения направления стрелок в произведениях: перевести грамматику «вычисления как синтаксический анализатор» в смысле этого Q в Thue можно было бы только изменить направления правил, затем программа Thue
дойдут
6

Мой ответ не предназначен быть формальным, точным и абсолютно тематическим. Я думаю, что ответ Марка Хаммана очень убедителен, но ваш вопрос заставил меня задуматься о смежной теме.

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

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

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

gasche
источник
Очень интересные замечания, хотя. Мой мозг слишком устал, чтобы давать хороший ответ прямо сейчас, однако в моем примере ветви дерева, по сути, представляют собой суб-вычисления, которые составлены вместе в соответствии с правилами синтаксического анализа ...
Рено Линдек,
6

Кроме того, можем ли мы построить программы, полные по Тьюрингу, указав грамматику…?

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

Артем Пеленицын
источник
1
Я понял вопрос следующим образом: Рено интересуется процессом синтаксического анализа начальной загрузки (определяемым грамматикой), который следует рассматривать как вычисление результата. Вычисление должно строить результат из частей, идущих в направлении, противоположном правилам производства грамматики. Правила переписывания Рефаля (IIUC, аналогично языку программирования Thue, упомянутому выше) будут идти в другом направлении (от ввода до результата).
imz - Иван Захарьящев
Теперь, когда я думаю об этом, контекстно-зависимые грамматики имеют более одного символа в LHS правил производства. Так что я думаю, что нет реальной практической разницы. Парсер для контекстно-зависимого языка - это система перезаписи строк, независимо от того, как вы на это смотрите, верно?
Рено Линдек
@imz спасибо за разъяснения по вопросу Рено. @Rehno «Парсер для контекстно-зависимого языка - это система переписывания строк, независимо от того, как вы на это смотрите, верно?» - возможно, это имеет смысл, да.
Артем Пеленицын
Но правила переписывания Рефаля трактуются недетерминированно? (Или по-другому: будет ли Refal выполнять возврат в поиске рабочего пути перезаписи?) Если мы хотим смоделировать этот подход «анализ как вычисление» с переписыванием правил в обратном направлении, нам нужны недетерминированные правила; рассмотреть грамматику как S -> A a; S -> B b; A -> 0; B -> 0. Если мы запрограммируем это путем изменения правил, нам нужно будет выбрать другие правила для обработки 0во время выполнения, чтобы вычислить «0a» или «0b» S.
imz - Иван Захарьящев
6

(Просто некоторые тривиальные соображения. Может быть комментарий, но слишком длинный.)

Фактически, то, что вы описываете, выглядит как очень естественное представление о том, что такое язык (в человеческом понимании «языка», его цели) и как грамматика определяет язык.

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

Если интерпретация вычислима, то синтаксические формы языка могут рассматриваться как программы, которые вычисляют семантические значения.

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

У некоторых теорий естественного языка есть такая форма (форма, которая согласуется с вышеупомянутыми соображениями; это уже упоминалось в ответе @ gasche здесь): дедуктивная система, которая ищет вывод ввода (в сочетании с вычислением семантического значение или построение проверочного термина; см. соответствие Карри-Хорварда). Итак, если мы посмотрим на подобные системы и рассмотрим их грамматики, то ваш вопрос тривиален : эти системы точно разработаны для выполнения вычислений описанным вами способом.

еграммя е(я)знак равноSяSграмм

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

imz - Иван Захарящев
источник
4

Просто добавить:

Чисто логическая программа имеет декларативное и процедурное чтение. В этом отчете обсуждается идея, что они могут быть дополнены грамматическим чтением, где пункты считаются правилами переписывания грамматики. Цель состоит в том, чтобы показать, что эта точка зрения облегчает передачу опыта от логического программирования к другим исследованиям языков программирования и наоборот. Некоторые примеры такой передачи обсуждаются. С другой стороны, представленное грамматическое представление оправдывает некоторые специальные расширения чистого логического программирования и облегчает разработку теоретических основ для таких расширений.

Грамматический взгляд на логическое программирование Пьера Дерансара и Яна Малушинского.

Vag
источник
По-видимому, Пролог возник из атрибутных грамматик, так что именно это представление положило начало логическому программированию.
reinierpost
1

Как насчет чего-то вроде чисел Пеано:

S    -> int
int  -> zero
int  -> succ
zero -> "0"
succ -> "#" int

он распознает любую строку (число) этой формы:

0   // zero
#0  // one
##0 // two

и он должен возвращать вложенную структуру с глубиной, равной числу.

Но это становится все сложнее, когда кто-то хочет реализовать просто скажем сложение:

S    -> int
int  -> sum
int  -> zero
int  -> succ
zero -> "0"
succ -> "#" int
sum  -> int "+" int

Это имеет смысл в том смысле, что он распознает только хорошо сформированные целые, как это:

#####0 + ####0

Но эта грамматика вводит расщепление в дереве разбора всякий раз, когда есть сумма, поэтому вместо того, чтобы иметь хорошее одноразветвленное дерево, которое напрямую отображается на число, мы имеем структуру выражения, все еще на расстоянии нескольких вычислений от эффективного значение. Так что никаких вычислений не делается, только распознавание. Беда может быть не в грамматике, а в парсере. Вместо этого можно использовать что-то другое, idk ... Еще один момент, который приходит на ум, - это адекватность грамматического формализма для выражения вычислений. Когда вы смотрите аксиому Пеано (в хаскеллоподобной записи):

1) Nat = Zero
2) Nat = Succ Nat
3) Sum ( Succ X ) ( Y ) = Succ ( X + Y )
4) Sum Zero X = X

Третье правило явно указывает на преобразование. Может ли кто-нибудь представить, что в контекстно-свободном грамматическом правиле есть смысл. И если да, то как?

dader
источник